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
typedefstruct
2
{
3
uint8_ta;
4
uint8_tb;
5
uint8_tc;
6
}MyStruct;
7
8
9
nunhabeich
10
MyStructStruktur_1;
11
MyStructStruktur_2;
12
MyStructStruktur_3;
mit Werten darin enthalten.
So jetzt könnte ich entweder eine Funktion schreiben, die alle Struct
derart einliest:
1
voidFunktion(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
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.
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...
> 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.
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*.
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.
Strukturen machen echt vieles einfacher, aber man muss erst einmal
durchsteigen, wann man wie auf welche Elemente zugreifen kann...
Danke für die Hilfe
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 ;-)
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.
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.
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.
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.
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