Forum: PC-Programmierung Auf Strukturen in Funktion zugreifen


von Struktur (Gast)


Lesenswert?

Hallo,

ich habe mehrere Strukturen und möchte in einer einzigen Funktion gerne 
alle Strukturen aufrufen und jeweils ein Element davon einlesen.

Die Strukturen sehen so aus:
1
typedef struct  
2
{
3
uint8_t a;
4
uint8_t b;
5
uint8_t c;
6
}MyStruct;
7
8
9
nun habe ich 
10
MyStruct Struktur_1;
11
MyStruct Struktur_2;
12
MyStruct Struktur_3;

mit Werten darin enthalten.


So jetzt könnte ich entweder eine Funktion schreiben, die alle Struct 
derart einliest:
1
void Funktion(MyStruct* X, MyStruct* Y, MyStruct* Z)

Dann müsste ich die Funktion aber im Code jedesmal mit den Strukturen in 
der richtigen Reihenfolge füttern, was ich vermeiden möchte weil 
fehleranfällig.


Gibt es noch eine zweite Möglichkeit in einer void Funktion(void) 
Darstellung auf alle Strukturen zu zu greifen, oder komme ich an die 
Daten innerhalb der Funktion nicht mehr ran, wenn sie nicht über die 
Pointer auf MyStruct übergeben werden?

Mit
1
void Funktion(void)
2
{
3
if(&Struktur_1->a == true)
4
...
5
}

bekomme ich den Fehler
1
invalid type argument of '->' (have MyStruct{aka struct <anonymous>}')
Das scheint also schonmal nicht der richtige Weg zu sein...

Danke!

: Verschoben durch Moderator
von Carl D. (jcw2)


Lesenswert?

Warum nicht einfach:
1
void Funktion(void)
2
{
3
if(Struktur_1.a == true)
4
...
5
}

Und wenn doch anders, dann scheint wohl der "->" Operator Vorrang zu 
haben.
-> Klammer!
1
void Funktion(void)
2
{
3
if((&Struktur_1)->a == true)
4
...
5
}

: Bearbeitet durch User
von db8fs (Gast)


Lesenswert?

Struktur schrieb:
> void Funktion(MyStruct* X, MyStruct* Y, MyStruct* Z)
> Dann müsste ich die Funktion aber im Code jedesmal mit den Strukturen in
> der richtigen Reihenfolge füttern, was ich vermeiden möchte weil
> fehleranfällig.

Was machst'n eigentlich Schönes? C oder C++? In letzterem kannste auch 
schön Referenzparameter nutzen, wenn du schon mit Call-By-Reference 
jeden einzelnen Parameter in einer Funktion verändern musst.

Ob daraus nämlich Code entsteht, den jemand mit vertretbaren Aufwand auf 
seine Funktionalität hin abtesten bzw. debuggen kann, sei mal 
dahingestellt.

> Gibt es noch eine zweite Möglichkeit in einer void Funktion(void)
> Darstellung auf alle Strukturen zu zu greifen, oder komme ich an die
> Daten innerhalb der Funktion nicht mehr ran, wenn sie nicht über die
> Pointer auf MyStruct übergeben werden?

Selbst wenn du 'nur' ANSI-C programmierst, gilt die Regel 'lokal geht 
vor global'. Besser lokal Variablen als aktuelle Parameter auf den Stack 
legen, als im gemeinsamen Speicher von jeder Funktion aus drinne rum 
mehren.

> Mitvoid Funktion(void)
> {
> if(&Struktur_1->a == true)
> ...
> }
>
> bekomme ich den Fehlerinvalid type argument of '->' (have MyStruct{aka
> struct <anonymous>}')Das scheint also schonmal nicht der richtige Weg zu
> sein...

Eine Variable besteht aus Datentyp, Adresse, Bezeichner und 
Zustand/Wert. Bezeichner != Datentyp.

von db8fs (Gast)


Lesenswert?

db8fs schrieb:
> Bezeichner != Datentyp.

Bitte ignorieren.

von Struktur (Gast)


Lesenswert?

aah, Danke.

Also mit einem Punkt-Operator reduziert sich der Error auf eine Warnung:

"comparison between pointer and integer"

Wenn ich einen Klammer Operator nutze, dann klappt es aber ohne Error.
Hoffentlich werden sie dann auch richtig übergeben...

von Carl D. (jcw2)


Lesenswert?

> void Funktion(MyStruct* X, MyStruct* Y, MyStruct* Z)
>
> Dann müsste ich die Funktion aber im Code jedesmal mit den Strukturen in
> der richtigen Reihenfolge füttern, was ich vermeiden möchte weil
> fehleranfällig.

Das ist eben nicht fehleranfällig, denn jeder Parameter hat einen 
anderen Typ. Da paßt der Compiler schon auf, daß kein "Pointer auf's 
Falsche" benutzt wird.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Struktur schrieb:
> Also mit einem Punkt-Operator reduziert sich der Error auf eine Warnung:
>
> "comparison between pointer and integer"

Weil du vermutlich noch den &-Operator drin hast.

Carl D. schrieb:
>> void Funktion(MyStruct* X, MyStruct* Y, MyStruct* Z)
>> ...
> Das ist eben nicht fehleranfällig, denn jeder Parameter hat einen
> anderen Typ.

Ich sehe da nur dreimal den gleichen Typ MyStruct*.

: Bearbeitet durch Moderator
von Carl D. (jcw2)


Lesenswert?

Yalu X. schrieb:
> Ich sehe da nur dreimal den gleichen Typ MyStruct*.

Was offensichtlich der Wahrheit entspricht. (schon das 2.Mal in 2Tagen 
wichtiges übersehen, :-((   )

Alternativvorschlag, wenn es darum geht x, y und z nicht zu verwechseln:
Nur eine Struktur, mit den Unterstrukturen x, y und z.

von Struktur (Gast)


Lesenswert?

Strukturen machen echt vieles einfacher, aber man muss erst einmal 
durchsteigen, wann man wie auf welche Elemente zugreifen kann...


Danke für die Hilfe

von Wilhelm M. (wimalopaan)


Lesenswert?

Struktur schrieb:

> So jetzt könnte ich entweder eine Funktion schreiben, die alle Struct
> derart einliest:
>
>
1
void Funktion(MyStruct* X, MyStruct* Y, MyStruct* Z)
>

Wenn es Input-Parameter sein soll, dann bitte:
1
void Funktion(const MyStruct* X, const MyStruct* Y, const MyStruct* Z)

oder in C++
1
void Funktion(const MyStruct& X, const MyStruct& Y, const MyStruct& Z)

Hat den Vorteil, dass keine dangling-Pointer möglich sind.

von Struktur (Gast)


Lesenswert?

oooh, was sind denn nun wieder dangling pointer, bzw. welches Phänomen 
wird damit beschrieben, unter welchen Umständen treten sie auf?

von Wilhelm M. (wimalopaan)


Lesenswert?

Oft wird der Fehler gemacht:
1
void foo(const int *p) {
2
   if(p) {
3
       use(*p);
4
   }
5
}
6
7
int main()  {
8
9
   int* ptr; // uninitialisiert = immer falsch
10
11
   foo(ptr); // ptr ist uninitialisiert, zeigt auf kein Objekt, 
12
13
   int a;
14
   foo(&a); // i.O.
15
16
   int* ptr2{0};
17
   foo(ptr2); // wird durch check-pointer idiom behandelt
18
19
}

ptr = dangling pointer

Solche Fehler wie oben können bei C++-Referenzen nicht passieren.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> oder in C++
> void Funktion(const MyStruct& X, const MyStruct& Y, const MyStruct& Z)
>
> Hat den Vorteil, dass keine dangling-Pointer möglich sind.

Höchstens dangling References ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Aber nicht vom caller zum callee!

Oder was meinstest Du?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Oder was meinstest Du?

Nehmen wir dein Beispiel von oben:

1
   int* ptr; // uninitialisiert = immer falsch
2
3
   foo(ptr); // ptr ist uninitialisiert, zeigt auf kein Objekt,

Wenn man foo gemäß deinem Vorschlag so ändert, dass eine Referenz statt
eines Pointers entgegengenommen wird, wird man den Aufruf in obigem Code
entsprechend in

1
   foo(*ptr);

ändern, damit er wieder passt. Das eigentliche Problem besteht aber nach
wie vor, nur dass jetzt an foo eben eine dangling Reference anstelle des
dangling Pointers übergeben wird.

Dennoch würde auch ich in diesem Fall für das Argument eine Referenz
einem Pointer vorziehen, aber aus anderen Gründen:

- Da die Struktur von foo nicht beschrieben wird, dient die Verwendung
  eines Pointers bzw. einer Referenz primär der Effizienz. Das muss man
  aber auf Aufrufseite nicht unbedingt sehen können, da eine Übergabe
  als Kopie (die in der Aufrufzeile syntaktisch genau gleich aussieht)
  semantisch keinen Unterschied macht.

- Bei der Übergabe als Referenz spart man in vielen Fällen ein paar &
  (bei den Aufrufen) und * (in der Funktion selbst) ein, was die ganze
  Sache etwas übersichtlicher macht.

Sollen die Argumente durch die aufgerufene Funktionen aber überschrieben
werden, verwende ich gerne Pointer statt Referenzen, weil man dann schon
beim Aufruf sofort einen Unterschied zur Übergabe per Kopie sieht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

> Sollen die Argumente durch die aufgerufene Funktionen aber überschrieben
> werden, verwende ich gerne Pointer statt Referenzen, weil man dann schon
> beim Aufruf sofort einen Unterschied zur Übergabe per Kopie sieht.

Es sollte eigentlich immer klar sein, das per-value Übergabe als 
pointer-to-non-const ein Output-Parameter wie auch Übergabe als 
non-const-reference, Übergabe als pointer-to-const oder const-reference 
ist zwingend ein Input-Parameter. Übergabe per-value klar input.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Es sollte eigentlich immer klar sein, das per-value Übergabe als
> pointer-to-non-const ein Output-Parameter wie auch Übergabe als
> non-const-reference

Das schon, nur sieht man das const in der Aufrufzeile leider nicht. Man
sieht aber, wenn da ein Pointer übergeben wird.

von Wilhelm M. (wimalopaan)


Lesenswert?

Das const muss man ja nicht sehen, es wird ja vom Compiler überprüft.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Das const muss man ja nicht sehen, es wird ja vom Compiler überprüft.

Ich möchte an einem Beispiel erläutern, wie ich das gemeint habe:

Angenommen, du stößt beim Überfliegen eines Ausschnitts eines fremden
Programms auf folgende Codezeile:

1
  midpoint(pointD, pointA, pointC);

Auf Grund des Namens der Funktion und der Argumente ahnst du, dass hier
der Mittelpunkt zwischen zwei gegebenen Punkten berechnet wird. Aber in
welchem der drei Argumente steht am Ende das Ergebnis?

Natürlich kannst du in der Softwaredokumentation nachschauen oder zur
Funktionsdefinition oder -deklaration springen. Beides unterbricht aber
deinen Lesefluss, und du wolltest das Programm ja nicht bis ins Detail
analysieren, sondern nur grob verstehen, was ein bestimmter Ausschnitt
tut.

Wie sähe es aber aus, wenn die Anweisung so

1
  midpoint(&pointD, pointA, pointC);

oder so

1
  midpoint(pointD, pointA, &pointC);

lauten würde? Dann siehst du sofort und ohne woanders nachschauen zu
müssen, dass das Ergebnisargument fast nur pointD bzw. pointC sein kann.

Das mag nach einer Kleinigkeit aussehen. Ist es auch. Aber es sind oft
solche Kleinigkeiten, die selbsterklärenden von erklärungsbedürftigem
Code unterscheiden.

Diese Methode ist natürlich nicht immer anwendbar, bspw. funktioniert
sie nicht für Arrays, da diese immer als Pointer übergeben werden. In
diesem Fall muss eben doch nachgeschlagen werden, ob das Array ein
Input- oder ein Output-Argument ist.

Meiner Meinung nach sollte man aber dort, wo mit einfachen Mitteln die
Selbstdokumentation eines Programms verbessert werden kann, diese auch
anwenden.

von C_Noob (Gast)


Lesenswert?

Guten Tag,

du könntest beispielsweise ein Array von Strukturen anlegen:

#define NUMBER_OF_STRUCTS   3

MyStruct struct[NUMBER_OF_STRUCTS];

Dann übergibst du deiner Funktion die Startadresse des Struct-Arrays, 
bearbeitest alle Elemente und zählst den Pointer um die Länge des 
Structs hoch. So könntest du mit einer for-Schleife alle Structs 
überprüfen (abhängig von NUMBER_OF_STRUCTS)

Viele Grüße

von Mr Struct (Gast)


Lesenswert?

Auch wenn die Antwort ein bisschen spät kommt, hier ein minimal Beispiel 
wie so etwas aussehen könnte:
1
typedef struct
2
{
3
  unsigned short u16_Age;
4
  unsigned short u16_pow;
5
}Person_t;
6
7
void print_structs(Person_t *p, unsigned long size)
8
{
9
  int x;
10
  for (x = 0; x < size; x++)
11
  {
12
    printf("Age: %d  Pow: %d\n", p->u16_Age, p->u16_pow);
13
    p++;
14
  }
15
}
16
17
int main()
18
{
19
  Person_t p[10];
20
  int x;
21
22
  for (x = 0; x < 10; x++)
23
  {
24
    p[x].u16_Age = x;
25
    p[x].u16_pow = x*x;
26
  }
27
28
  print_structs(p, sizeof(p)/sizeof(Person_t));
29
}

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.