Ein Struct fasst mehrere Variablen zu einem 'Paket' zusammen. Die
Variablen können dazu auch unterschiedliche Typen haben.
Ein Array ist eine Gruppe gleicher Variablen, deren einzelne Elemente
über den Index (Position) anzusprechen sind.
Die Frage ist wie:
"Welcher Vorteil hat eine Hose gegenüber einem Pullover?"
Du brauchst ein (C?) Buch
Die Frage ist so sinnvoll wie: Welchen Vorteil hat ein Ei gegenüber
einer Pappschachtel?
Was Structs sind, und was Array, steht in deinem C-Buch. Beide haben
ihren Anwendungsbereich, der sich kaum überschneidet.
Oliver
L. R. schrieb:> Hei,>> welchen Vorteil haben "struct" gegenüber einzelne mehrspaltige Array's ?>
Ein struct ermöglicht es dir, einen logischen Zusammenhalt mehrerer
Werte explizit auszudrücken und den Dingen einen Namen zu geben.
Ein Datum besteht nun mal aus Tag/Monat/Jahr
struct datum_
{
uint8_t Tag;
uint8_t Monat;
uint16_t Jahr;
};
Eine Uhrzeit besteht aus Stunde/Minute/Sekunde
struct zeit_
{
uint8_t Stunde;
uint8_t Minute;
uint8_t Sekunde;
}
Der komplette Name einer Person besteht aus Vorname und Familienname
struct name_
{
char Vorname[20];
char Familienname[30];
}
Eine Ortsangabe besteht aus Postleitzahl, Ortsname, Strasse und
Hausnummer
struct ort_
{
char PLZ[6];
char Name[30];
char Strasse[30];
char Nummer[5];
};
und eine komplette Personenbeschreibung besteht aus dem kompletten
Namen, einem Wohnort, seinem Geburtstag, seinem Hochzeitstag.
struct person
{
struct name_ Name;
struct date_ Geburtstag;
struct date_ Hochzeitstag;
struct ort_ Wohnort;
};
siehst du, wie sich die Beschreibung einer Person mit den vorhergehenden
Strukturen ganz automatisch strukturiert? Eine Person Franz
struct person_ Franz;
hat damit automatisch einen Monat in dem er geboren wurde. Er "erbt"
dies, weil er einen Geburtstag hat und dieser als struct date_
klarerweise über ein Monat verfügt. Der Monat in dem Franz geboren wurde
ist daher
Franz.Geburtstag.Monat
lies es von links nach rechts und das ganze ist weitgehend
selbsterklärend. Vom Allgemeineren zum immer Spezifischeren.
Und wenn du zum struct ort_ noch das Land mit dazunimmst, dann 'erben'
alle weiteren Strukturen oder Funktionen, die mit einem struct ort_
arbeiten ganz automatisch, dass ein struct ort_ jetzt auch noch das Land
oder den Staat mit beinhaltet.
Musst du 300 Personen speichern können (zb die Belegschaft einer Firma),
dann eben
struct person_ Belegschaft[300];
und du hast entsprechenden Speicher bereitsgestellt um 300 Personen
speichern zu können. Mit allem drum und dran. Inklusive Wohnort,
inklusive Geburtstag, inklusive eben allem was eine Person ausmacht.
Jede einzelne Person deiner Belegschaft hat einen Geburtsmonat. Denn im
Array sind ja lauter Personen gespeichert und jede ist vom Typ struct
person_ und hat als solches einen Geburtstag.
Schreibst du dir daher eine Funktion, die einen beliebiges struct datum_
ausgeben kann
Das muss aber nicht so sein. Würdest du für eine Person in deiner
Belegschaft zb auch noch das Datum mitspeichern, an dem es in die Firma
gekommen ist.
1
structperson
2
{
3
structname_Name;
4
structdate_Geburtstag;
5
structdate_Hochzeitstag;
6
structdate_Firmeneintritt;
7
structort_Wohnort;
8
};
dann kannst du selbstverständlich die Funktion printDate auch dazu
benutzen, dieses Datum auszugeben. Denn auch ein 'Firmeneintritt' ist ja
nur ein struct date_
Oder wenn du Basisfunktionen hast, die zb mit einem struct date_ diverse
Vergleiche oder Berechnungen anstellen, dann kannst du damit zb
wiederrum Funktionen schreiben, die alle Mitarbeiter rausfiltern, die
heuer 30 Jahre in der Firma sind, oder die gerade ihren 25. Geburtstag
feiern etc, etc.
Aus einer 'logischen Gruppierung', in der man Dinge die zusammengehören
auch in Form einer Datenstruktur zusammenfasst und strukturiert, folgt
also wesentlich mehr als du jemals mit mehrspaltigen Arrays ausdrücken
könntest. Du verpasst damit deinen Daten eine Struktur die diesem
logischen Zusammenhalt entspricht und versetzt dich damit in die Lage,
dein reale Welt besser und übersichtlicher und vor allen Dingen in
'deiner gewohnten Sprache' in einem Programm abbilden zu können. Wieviel
aussagekräftiger ist
Belegschaft[i].Geburtstag.Monat
als
Belegschaft[i][12]
nur weil du zufällig weißt, dass in der Spalte 12 das Geburtsmonat
gespeichert ist?
Zudem ermöglichst du es dir, dass du dich bei der Programmierung auf
Teilbereiche konzentrieren kannst. Wenn du dir Funktionen machst, die
mit einem struct date_ arbeiten (egal ob Ausgeben, Vergleichen, Tage
zwischen Daten berechnen, etc...), du konzentrierst dich zu diesem
Zeitpunkt nur und ausschliesslich auf das, was mit einem struct date_
möglich sein soll und wie diese Funktionalität zu implementieren ist.
Das dieses struct date_ dann in Form von Geburtstagen, Firmeneintritten,
Jubiläen etc. benutzt wird bzw. werden kann, ist dir beim Schreiben der
Datumsfunktionen herzlich egal - und das soll es auch sein. Natürlich
wird deine Entwicklung dieser Funktionen dadurch getrieben werden, weil
du weißt, was du an Funktionalität brauchen wirst. Der springende Punkt
ist aber, dass die entstehenden Funktionen erst mal damit nichts zu tun
haben. Sie wären auch abseits davon nützlich.
Mal ganz davon abgesehen, dass du mit einer struct beliebige Datentypen
zu größeren Einheiten zusammenfassen kannst, während ein Array immer nur
von einem Datentyp sein kann.
@Karl Heinz Buchegger (kbuchegg):
Das ist ein toller Beitrag! Eventuell könnte man das in einen
Wiki-Artikel gießen. Hier wird er womöglich verstauben.
Es gibt zwei wesentliche Unterschiede zwischen Structs und Arrays:
1. Ein Array ist eine Ansammlung von Daten gleichen Typs, in einem
Struct können beliebige Datentypen gemischt werden. Will man also
mehrere unterschiedliche Datentypen zu einem neuen zusammenfassen,
nimmt man ein Struct.
2. Bei einem Struct werden die einzelnen Elemente über Namen addres-
siert, bei einem Array über numerische Indizes. Will man also über
alle Elemente iterieren oder auf das n-te Element zugreifen (wobei n
das Ergebnis einer vorangegangenen Berechnung ist), nimmt man ein
Array.
Wird die numerische Indizierung nicht benötigt, ist meist (auch bei
gleichen Datentypen) das Struct die bessere Wahl, da die Adressierung
der Elemente über Namen auch einen Beitrag zur Selbstdokumentation des
Codes liefert. Dazu hat ja Karl Heinz ein schönes Beispiel gebracht.
Obacht, das kann --je nach Strukturgröße-- einiges an Rechenzeit
erfordern.
Und beim Funktionsaufruf ist darauf ebenfalls Rücksicht zu nehmen:
1
voidmachwas(structblafoo)
2
{
3
foo.x=3;
4
foo.y=4;
5
foo.z=2.34;
6
}
7
8
9
structblaa;
10
11
a.x=1;
12
a.y=2;
13
a.z=1.23;
14
15
machwas(a);
16
17
printf("%d\n",a.x);
Hier wird die Struktur der Funktion als Kopie übergeben, d.h. nicht
nur, daß die Zuweisungen in der Funktion nur die Kopie verändern und die
nach dem Funktionsaufruf folgende printf -Anweisung den Wert "1" und
nicht "3" ausgibt,
ist auch zu berücksichtigen, daß diese Kopie auf dem Stack angelegt
wird, was bei entsprechend großen Strukturen zu interessanten
Nebenwirkungen führen kann.
@ Johann L. (gjlayde) Benutzerseite
>3. Strukturen werden in C immer "per value" übergeben> während für Arrays die "decay to pointer" Regel gilt.
Hab ich da was verpasst? Ich kann doch genausogut einen Pointer auf eine
Struktur an eine Funktion übergeben.
Falk Brunner schrieb:> @ Johann L. (gjlayde) Benutzerseite>>>3. Strukturen werden in C immer "per value" übergeben>> während für Arrays die "decay to pointer" Regel gilt.>> Hab ich da was verpasst? Ich kann doch genausogut einen Pointer auf eine> Struktur an eine Funktion übergeben.
oder andersrum - du kannst kein array per value übergeben.
Falk Brunner schrieb:> @ Johann L. (gjlayde) Benutzerseite>>>3. Strukturen werden in C immer "per value" übergeben>> während für Arrays die "decay to pointer" Regel gilt.>> Hab ich da was verpasst? Ich kann doch genausogut einen Pointer auf eine> Struktur an eine Funktion übergeben.
Klar kannst du das — wenn du die Adresse nimmst per &. Wenn du aber
schreibst
1
typedefstruct
2
{
3
unsignedr,g,b;
4
}rgb_t;
5
6
externintget_r(rgb_t);
7
8
rgb_trgb;
9
10
intmain(void)
11
{
12
returnget_r(rgb);
13
}
dann wird rgb per value übergeben und nicht etwa die Adresse von rgb wie
es bei einem Array der Fall wäre.
Hei,
vielen Dank für die ausführlichen Informationen.
zuletzt noch die Frage zur Verarbeitungszeiten.
z.B.:
struct
{
unsigned int a[32];
..
unsigned int y[32];
unsigned int ab[32][8]
} my_xy
als einfache Array's
unsigned int a(32);
..
unsigned unt y(32);
unsigned int ab[32][8]
welches Modell benötigt die geringste Zeit, um eine Varialbe zu lesen,
schreiben, vergleichen
schönen Tag
L. R. schrieb:> welches Modell benötigt die geringste Zeit, um eine Varialbe zu lesen,> schreiben, vergleichen
Das sollte gleich sein. Schau Dir mal den Assembler-Output an.
Falk Brunner schrieb:> @ Johann L. (gjlayde) Benutzerseite>>>3. Strukturen werden in C immer "per value" übergeben>> während für Arrays die "decay to pointer" Regel gilt.>> Hab ich da was verpasst? Ich kann doch genausogut einen Pointer auf eine> Struktur an eine Funktion übergeben.
Klar. Dann übergibst du aber eben keine Struct, sondern einen Pointer.
Bei einem Array wird aber, wenn du dieses übergibst, automatisch ein
Pointer draus gemacht.
@ Rolf Magnus (rmagnus)
>>>3. Strukturen werden in C immer "per value" übergeben>>> während für Arrays die "decay to pointer" Regel gilt.>Klar. Dann übergibst du aber eben keine Struct, sondern einen Pointer.>Bei einem Array wird aber, wenn du dieses übergibst, automatisch ein>Pointer draus gemacht.
Dann ist doch aber die Aussage falsch, "Strukturen werden in C immer
"per value" übergeben". Eher so.
Strukturen kann man per Value oder Pointer übergeben, Arrays nur per
pointer.
Wenn man ein & vor die Struktur schreibt ist es ja keine Struktur
mehr...
Wenn man einen Zeiger auf die Struktur übergibt, dann muß man das
händisch mit dem Adress-Operator machen und explizit hinschreiben; es
gibt keinen eintsprechenden Mechanismus in C der das automatisch
machen würde.
Und dies ist dann auch etwas anderes als eine Referenz in C++.
Falk Brunner schrieb:> Strukturen kann man per Value oder Pointer übergeben, Arrays nur per> pointer.
Ist ein Streit um des Kaisers Bart bezüglich Aussagen.
in
1
voidfoo(int*pI)
2
{
3
...
4
}
5
6
intmain()
7
{
8
intj;
9
10
foo(&j);
11
}
man kann sagen
"es wird nicht der int j selber übergeben, sondern ein Pointer."
Punkt.
Das dieser Pointer zufällig auf j zeigt ist nett, hat aber nichts damit
zu tun, dass da ein int in einer speziellen Form in die Funktion
hineinwandert (und aus einem speziellen Grund). Denn im eigentlichen
Sinne wird da ja ein Pointer (eigentlich "eine Adresse") übergeben. Und
eine Adresse ist nun mal kein int und hat so gesehen auch erst mal
nichts mit einem int zu tun.
Es ist deswegen ein Streit um des Kaisers Bart, weil das natürlich so
stimmt. Da wird kein int übergeben sondern eine Adresse (Pointer). Tritt
man aber einen Schritt zurück und sieht sich die Sache aus der Ferne an,
dann steckt da natürlich eine bestimmte Absicht dahinter, in der dieses
'Übergabeschema' eine bestimmte Bedeutung hat. Man übergibt ja nicht aus
Jux und Tollerei da eine Adresse, sondern man bezweckt damit ja etwas.
Und aus dieser Warte heraus unterscheidet man dann zwischen 'Passing per
Value' und 'Passing per Pointer'. Für den Compiler hingegen gibt es
diese Unterscheidung nicht. Für den gibt es nur: Ein Wert geht in die
Funkion hinein - Pass per Value. Wenn der Programmierer einen 'Pass per
Pointer' machen will, dann muss er das selber machen: Adresse bestimmen,
Adresse 'per Value' an die Funktion geben, in der Funktion dann über
diese Adresse auf das eigentliche Teil zugreifen.
In anderen Sprachen ist das anders. Da unterstützt der Compiler
verschiedene Übergabemechanismen, so dass ich mich als Programmierer da
nicht über die Details Gedanken machen muss. In C gibt es so gesehen
eigentlich nur den 'Pass per Value', den ich natürlich auch dazu
benutzen kann, eine Adresse per Value zu übergeben.
Karl Heinz Buchegger schrieb:> In C gibt es so gesehen> eigentlich nur den 'Pass per Value',
was allerdings im Falle eines Arrays ein "gefühltes" 'Pass per pointer'
ergibt.
Das Array selber kann man nicht per value übergeben, weil die
Arrayvariable nur der Pointer auf das Array ist.
Oliver
Oliver schrieb:> Karl Heinz Buchegger schrieb:>> In C gibt es so gesehen>> eigentlich nur den 'Pass per Value',>> was allerdings im Falle eines Arrays ein "gefühltes" 'Pass per pointer'> ergibt.
Stimmt schon.
Das liegt aber weniger daran, dass es ein 'Pass per pointer' geben würde
sondern daran, dass Arrays grundsätzlich in der Sprache anders behandelt
werden.
Bei jedem anderen Datentyp/Datenstruktur bewirkt die Nennung des
Variablennamens, dass der Inhalt der Variablen gemeint ist. Nur bei
Arrays ist das anders. Die Nennung eines Arraynamens meint immer die
Startadresse des Arrays. Auch bei Zuweisungen ist das nicht anders.
int i, j;
i = j;
Die Nennung von j zielt auf den 'Inhalt von j' ab.
int i, k[5];
i = k[3];
hier passiert nichts Aussergewöhnliches. Für den Compiler existieren nur
2 Regeln
* die Operation a[i] muss umgeformt werden zu *(a + i)
* die Nennung eines Arraynamens zielt auf die Startadresse des
Arrays ab (und nicht auf den Inhalt)
aus k[3] wird aufgrund der ersten Regel
*(k + 3)
und da das k hier der Name eines Arrays ist, ist damit die Startadresse
des Arrays gemeint (und nicht etwa der Wert einer Variablen). Und in
Summe (und den Regeln der Pointerarithmetik) ergibt das dann eben den
Arrayzugriff per Index, wie wir ihn gewohnt sind.
Arrays sind Bastarde in C, weil sie grundsätzlich andres funktionieren.
Steht bei anderen Datentypen der Name der Variable für ihren Wert, so
steht er bei Arrays für die Startadresse der Variable. Und das ist für
den Compiler immer so, nicht nur bei Argument Passing. Daraus heraus,
und ein paar zusätzlichen Regeln, ergibt sich dann die gewohnte
Array-Indizierung bzw. es sieht für uns so aus, als ob es für Arrays
speziell beim Argument Passing eine Ausnahme zur gewohnten
Array-Verwendung gibt. Aber eigentlich stimmt das nicht. Denn die
C-Regeln sind so formuliert, dass man im Zusammenhang mit Arrays nur
diese eine Regel braucht: Die Nennung eines Array namens bezeichnet die
Startadresse des Arrays.
Karl Heinz Buchegger schrieb:> Steht bei anderen Datentypen der Name der Variable für ihren Wert, so> steht er bei Arrays für die Startadresse der Variable. Und das ist für> den Compiler immer so, nicht nur bei Argument Passing.
Fast immer. Ausnahmen:
1
sizeofarray
und
1
&array
Funktionszeiger und -designatoren sind übrigens mindestens genauso
bastardös :)