Forum: PC-Programmierung Auf Strukturen in Funktion zugreifen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Struktur (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert
db8fs schrieb:
> Bezeichner != Datentyp.

Bitte ignorieren.

von Struktur (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
2 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert
Aber nicht vom caller zum callee!

Oder was meinstest Du?

von Yalu X. (yalu) (Moderator)


Bewertung
1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


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

von Yalu X. (yalu) (Moderator)


Bewertung
2 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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
}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.