Forum: Compiler & IDEs (void*) Zeiger auf unterschiedliche Strukturen


von Martin (Gast)


Lesenswert?

Hallo zusammen,

in meinem Programm habe ich drei ähnliche Strukturen:
1
struct 
2
{    
3
  BYTE  Len;          
4
  BYTE  Index[48];  
5
  BYTE  Data[48][16];      
6
7
} MailBox1;
8
9
10
struct 
11
{    
12
  BYTE  Len;          
13
  BYTE  Index[32];  
14
  BYTE  Data[32][16];      
15
16
} MailBox2;
17
18
struct 
19
{    
20
  BYTE  Len;          
21
  BYTE  Index[16];  
22
  BYTE  Data[16][16];      
23
24
} MailBox3;
Im Laufzeit wird nur eine Struktur von den Drei benutzt (hängt von einer 
Hw Adresse ab), die Daten kommen vom Master und müssen in der richtigen 
MailBox kopiert, bearbeitet und anschließend weiter gesendet.

Wie kann ich am besten /elegantesten eine universelle Funktion 
programmiere, die alle Typen "gleich" behandelt ohne eine switch /case 
oder doppelten Code?

Ich habe an void* Ptr gedacht, aber das hilft nicht viel, da ich die 
ganze Zeit den richtigen Strukturtyp kennen muss => also doch eine 
switch/case.

: Verschoben durch User
von Karl H. (kbuchegg)


Lesenswert?

Martin schrieb:

> Wie kann ich am besten /elegantesten eine universelle Funktion
> programmiere, die alle Typen "gleich" behandelt ohne eine switch /case
> oder doppelten Code?

So gar nicht.

Was noch gehen würde, ist in deiner Mailbox Pointer auf die 
tatsächlichen Arrays zu haben. Dann hast du nur eine 'struct Mailbox' 
und je nach tatsächliche benötigter Instanz, zeigen die Pointer dann auf 
unterschiedlich große Arrays.
1
struct 
2
{    
3
  BYTE  Len;          
4
  BYTE *Index;  
5
  BYTE (*Data)[][16];
6
} MailBox2;

(Der Pointer auf das 2D Array musste IMHO so vereinbart werden, aber 
100% sicher bin ich mir da jetzt auch nicht, was die Syntax angeht. Zur 
Not kann man das immer noch als einfaches 1D Array entsprechender Größe 
ansehen und sich selbst um die Indizierung kümmern.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

1
struct 
2
{    
3
  BYTE  Len;          
4
  BYTE  Bytes[48+48*16];  
5
} MailBox;
Ohne Fallunterscheidung geht es dann arithmetisch ab, indem Len (oder 
was immer Basis für die Fallunterscheidung ist) in die Berechnung des 
Index von Bytes[] eingeht. Ob das freilich sinnvoller als eine 
Fallunterscheidung ist, ist eine andere Frage.

Mit Fallunterscheidung würde ich dir statt void* eine union als Herz 
legen. Dafür sind die da.

: Bearbeitet durch User
von DirkB (Gast)


Lesenswert?

A. K. schrieb:
> Mit Fallunterscheidung würde ich dir statt void* eine union als Herz
> legen. Dafür sind die da.

Die union ist dann (hier) aber so groß, wie die größte struct.
Da kann man dann gleich nur eine struct nehmen.

von B. S. (bestucki)


Lesenswert?

DirkB schrieb:
> Die union ist dann (hier) aber so groß, wie die größte struct.
> Da kann man dann gleich nur eine struct nehmen.

Eine Union macht in diesem Fall keinen wirklichen Sinn, da die Struktur 
MailBox1 alle Daten aufnehmen kann, die in MailBox2 und MailBox3 
gespeichert werden können. Dazu einfach MailBox1 erweitern und die 
entsprechende Funktion anpassen:
1
struct
2
{
3
  BYTE  Len;
4
  BYTE  Index[48];
5
  size_t IndexSize;
6
  BYTE  Data[48][16];
7
  size_t DataSize; /* gilt nur fuer die erste Dimension */
8
} MailBox1;


Eine Union würde hingegen Sinn machen, wenn nur wenige Variablen des 
Typs MailBox1, aber viele von MailBox3 benötigt würden und der Speicher 
optimal ausgenutzt werden müsste (ich hoffe, ich hab mich nicht 
verrechnet):
1
typedef union{
2
  MailBox1 Box1[2]; /* 1634 Bytes (ohne Padding) */
3
  MailBox2 Box2[3]; /* 1635 Bytes (ohne Padding) */
4
  MailBox3 Box3[6]; /* 1650 Bytes (ohne Padding) */
5
}MailBoxes;

von Karl H. (kbuchegg)


Lesenswert?

Ich denke, A.K. meinte eine union aus 3 verschiedenen Pointern anstelle 
eines einzigen void*

Das eliminiert zwar nicht den Zugriff über switch-case Strukturen, ist 
aber immer noch besser als ein void* in einer Funktionsargumentliste.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz schrieb:
> Ich denke, A.K. meinte eine union aus 3 verschiedenen Pointern anstelle
> eines einzigen void*

Nein. Ich verstehe die Aufgabe so, dass der Controller im Rahmen seiner 
Programmausführung auf eine der 3 Varianten fixiert ist. Aber welche das 
ist liegt erst zu Laufzeit fest. Oben steht dazu etwas von einer 
Hardware-Adresse, was immer das sein soll, die darüber entscheidet. Kann 
man sich vielleicht auch als DIP-Switch vorstellen.

Wenn diese Struktur einfach vorkommt handelt es sich um die klassische 
Aufgabe von Unions. Der zu reservierende Platz entspricht natürlich der 
grössten davon, aber das liegt in der Natur der Aufgabe.

Anders wird es, wenn diese Struktur dynamisch verwaltet werden muss oder 
als Element eines Arrays verwendet wird.

--

Zum eigentlichen Problem: Das artet schnell in Kaffeesatzlesen aus, weil 
zu wenig über die Verarbeitung bekannt ist. Ohne Fallunterscheidung zu 
arbeiten lohnt nur, wenn sich einzig die Positionen der Felder 
unterscheiden, nicht aber die Verarbeitung. Dann aber könnte ein Pointer 
(oder ein Basis-Index) dienlich sein, um in Bytes[] (mein Beispiel oben) 
den Anfang von Data[] zu kennzeichnen. Nur dieser Pointer muss dann 
fallbasiert einmal gesetzt werden.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Karl Heinz schrieb:
>> Ich denke, A.K. meinte eine union aus 3 verschiedenen Pointern anstelle
>> eines einzigen void*
>
> Nein.

Dann muss ich den anderen recht geben, dass eine union eine schlechte 
Lösung ist und ich hätte meinen Einwand dahingehend am Vormittag nicht 
zurück ziehen brauchen.

Denn dann erhebt sich die Frage: Wozu überhaupt 3 verschiedene 
Strukturen, wenn alle sowieso letzten Endes eingepfercht in die union 
den gleichen Platz verbrauchen.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Karl Heinz schrieb:
> Denn dann erhebt sich die Frage: Wozu überhaupt 3 verschiedene
> Strukturen, wenn alle sowieso letzten Endes eingepfercht in die union
> den gleichen Platz verbrauchen.

Das war ja auch mein erster Ansatz mit der arithmetischen Variante. Oder 
wie grad eben skizziert mit Pointer:
1
struct 
2
{    
3
  BYTE  Len;          
4
  BYTE  Bytes[48+48*16];  
5
} MailBox;
6
BYTE (*Data)[16] = (BYTE (*)[16])&Mailbox.Bytes[DaWoDiesmalDataAnfängt];
und dann mit Data[n][k] statt MailboxX.Data[n][k] arbeiten.

Die Union ergibt dann Sinn, wenn nicht nur das Layout verschieden ist, 
sondern sich auch die Verarbeitung der Daten signifikant unterscheidet. 
Das geht nicht klar aus der Aufgabe hervor.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Karl Heinz schrieb:
>> Denn dann erhebt sich die Frage: Wozu überhaupt 3 verschiedene
>> Strukturen, wenn alle sowieso letzten Endes eingepfercht in die union
>> den gleichen Platz verbrauchen.
>
> Das war ja auch mein erster Ansatz mit der arithmetischen Variante.

Hab ich gesehen.
Daher dachte ich ja auch, du hättest in der Antwort einfach nur das Wort 
Pointer vergessen

> Mit Fallunterscheidung würde ich dir statt void* eine union als Herz legen.

denn ein
1
typedef union MailBoxPtr_
2
{
3
  MailBox1* pMail1;
4
  MailBox2* pMail2;
5
  MailBox3* pMail3;
6
} MailBoxPtr;
7
8
9
void foo( uint8_t Type, MailBoxPtr theMail )
10
{
11
  ...
12
}

ist immer noch besser als ein
1
void foo( uint8_t Type, void * theMail )
2
{
3
  ...
4
}

in welches ich jeden dahergelaufenen Pointer reinstopfen kann,
1
    foo( 1, "Hallo World" );
ohne dass es den Compiler auch nur im geringsten juckt.
Mit einer Pointer union könnte man wenigstens noch ein bischen 
Typsicherheit retten, wenn man auch um die Fallunterscheidung nicht 
rumkommt.

: Bearbeitet durch User
von LUGS (Gast)


Lesenswert?

Karl Heinz schrieb:
> in welches ich jeden dahergelaufenen Pointer reinstopfen kann

Das ist aus meiner Sicht die beste Lösung. Und eine union braucht es 
dazu nicht, wenn man in der Fallunterscheidung mit Distanzen rechnet.
Das ist effizient und schneller als mit unions und types zu 
unterscheiden.

Martin schrieb:
> BYTE  Data[48][16];

Würde mich interessieren, warum Du so komplex denkst. Linear ist viel 
schneller. Der Compiler macht letzten Endes auch nichts anderes daraus.
Also warum nicht:
1
typedef struct {
2
3
char *prev, *next, *data;
4
} Mailbox_t;

Und per malloc bei Bedarf den notwendigen Speicher entsprechend 
anfordern.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.