Forum: Compiler & IDEs struct contra Array


von L. R. (keyboard)


Lesenswert?

Hei,

welchen Vorteil haben "struct" gegenüber einzelne mehrspaltige Array's ?

schönen Tag

von Udo S. (urschmitt)


Lesenswert?

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

von Oliver (Gast)


Lesenswert?

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

von Di P. (drpepper) Benutzerseite


Lesenswert?

Udo Schmitt schrieb:
> Die Frage ist wie:
> "Welcher Vorteil hat eine Hose gegenüber einem Pullover?"

YMMD

von Karl H. (kbuchegg)


Lesenswert?

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
1
void printDate( struct date_ * Datum )
2
{
3
  printf( "%d/%d/%04d", Datum->Tag, Datum->Monat, Datum->Jahr );
4
}

dann kannst du, weil ja ein Geburtstag einer Person so ein Datum ist, 
die Geburtstage deiner kompletten Belegschaft ausgeben
1
  printf( "Geburtstage\n" );
2
3
  for( i = 0; i < 300; i++ ) {
4
    printf( "%s %s: ", Belegschaft[i].Name, Belegschaft[i].Vorname );
5
    printfDate( & Belegschaft[i].Geburtstag );
6
    printf( "\n" );
7
  }

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
struct person
2
{
3
  struct name_ Name;
4
  struct date_ Geburtstag;
5
  struct date_ Hochzeitstag;
6
  struct date_ Firmeneintritt;
7
  struct ort_  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_
1
  printf( "Firmeneintritte\n" );
2
3
  for( i = 0; i < 300; i++ ) {
4
    printf( "%s %s: ", Belegschaft[i].Name, Belegschaft[i].Vorname );
5
    printfDate( & Belegschaft[i].Firmeneintritt);
6
    printf( "\n" );
7
  }

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.

von Di P. (drpepper) Benutzerseite


Lesenswert?

@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.

von Falk B. (falk)


Lesenswert?

Karl Heinz, du solltest DRINGEND ein Buch schreiben. Oder wenigstens ein 
paar Artikel. Sonst sind deine Werke verschwendet.

von Karl H. (kbuchegg)


Lesenswert?

Dem steht meine Faulheit entgegen, das alles etwas sauberer und 
ausführlicher auszuformulieren. Noch kann ich mich da nicht aufraffen 
:-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

3. Strukturen werden in C immer "per value" übergeben
   während für Arrays die "decay to pointer" Regel gilt.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

4. Strukturzuweisungen sind möglich:
1
struct bla
2
{
3
  int x;
4
  int y;
5
  double z;
6
};
7
8
struct bla a;
9
struct bla b;
10
11
a.x = 1;
12
a.y = 2;
13
a.z = 1.23;
14
15
b = a;

Obacht, das kann --je nach Strukturgröße-- einiges an Rechenzeit 
erfordern.

Und beim Funktionsaufruf ist darauf ebenfalls Rücksicht zu nehmen:
1
void machwas(struct bla foo)
2
{
3
  foo.x = 3;
4
  foo.y = 4;
5
  foo.z = 2.34;
6
}
7
8
9
struct bla a;
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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Peter II (Gast)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> oder andersrum - du kannst kein array per value übergeben.

doch - in einer struct :-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
typedef struct
2
{
3
  unsigned r, g, b;
4
} rgb_t;
5
6
extern int get_r (rgb_t);
7
8
rgb_t rgb;
9
10
int main (void)
11
{
12
    return get_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.

von L. R. (keyboard)


Lesenswert?

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

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

L. R. schrieb:
> welches Modell benötigt die geringste Zeit, um eine Varialbe zu lesen,
> schreiben, vergleichen

lesen natürlich

von Rolf M. (rmagnus)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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++.

von Karl H. (kbuchegg)


Lesenswert?

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
void foo( int * pI )
2
{
3
  ...
4
}
5
6
int main()
7
{
8
  int  j;
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.

von Oliver (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von gfhf (Gast)


Lesenswert?

aber den inhalt einer zelle des arrays  per value...
1
int array[100];
2
3
 void foo( int a )
4
{
5
  ...
6
}
7
8
int main()
9
{
10
11
  foo( array[i] );
12
}

von gfhf (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Arrays sind Bastarde in C,

 ^^

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
sizeof array

und
1
&array

Funktionszeiger und -designatoren sind übrigens mindestens genauso
bastardös :)

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.