Seite 1 von 1

Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Di Jan 15, 2013 9:22 pm
von forumnewbie
Hallo!

ich habe eine Frage zu diesem Beispiel ( http://www.proggen.org/doku.php?id=c:tutorial:struct ):

Code: Alles auswählen

int main( void )
{
  struct Person   personen[10];
  struct Person * weiterePersonen;
 
  weiterePersonen = (struct Person*) malloc( 10 * sizeof( struct Person ));
  if( weiterePersonen )
  {
    /* ... */
 
    callByPointer( &weiterePersonen[0] );
 
    free( weiterePersonen );
  }
 
  callByPointer( &personen[0] );
 
  return 0;
}
Warum soll man einen Zeiger * weiterePersonen erstellen, der nicht auf meine Strukturvariable personen zeigt und danach noch so viel Platz für den Zeiger reservieren? Bei diesem Schritt

Code: Alles auswählen

struct Person   personen[10];
wird ja automatisch im im Arbeitsspeicher Platz für 10x struct Person angelegt, egal ob wir diese nutzen oder nicht. Und man kann dann die Daten sofort an die Membervariablen übergeben, mit z.B.

Code: Alles auswählen

strcpy( personen[9].firstName, "Hans2" );
Ich habe deshalb leider den Sinn von diesem Zeiger * weiterePersonen nicht verstanden.

Und dann gleich noch eine Frage zu free(). Wenn ich mit free() eine reservierte Zeiger-Adresse freigebe, dann kann ich danach trotzdem problemlos auf die Daten mittels diesem Zeiger zugreifen. Bedeutet das Wort Freigeben an dieser Stelle, dass die Daten irgendwann überschrieben werden können, aber nicht müssen?

EDIT: ich glaube, dass ich meine erste Frage selbst beantworten kann.
Mit dem Zeiger * weiterePersonen + malloc legen wir ein weiteres neues Array an, welches die selbe Größe besitzt wie unser Array personen vom Datentyp struct Person. Das sind aber zwei unterschiedliche Arrays mit unterschiedlichen Daten, richtig?
Dann hätte ich eine andere Frage:

Code: Alles auswählen

struct Person
{
    char           firstName[64];
    char           familyName[64];

    unsigned int   age;

    struct Address mainAddress;
    struct Address secondaryAddress;
};
Warum darf ich nicht statt:

Code: Alles auswählen

struct Address mainAddress;
folgendes machen bzw. warum funktioniert das nicht?

Code: Alles auswählen

struct Address *mainAddress;
mainAddress = malloc(sizeof(struct Address));

Vielen Dank!

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Di Jan 15, 2013 10:15 pm
von nufan
Hallo und willkommen im Forum :)
forumnewbie hat geschrieben:EDIT: ich glaube, dass ich meine erste Frage selbst beantworten kann.
Mit dem Zeiger * weiterePersonen + malloc legen wir ein weiteres neues Array an, welches die selbe Größe besitzt wie unser Array personen vom Datentyp struct Person. Das sind aber zwei unterschiedliche Arrays mit unterschiedlichen Daten, richtig?
Richtig erkannt. In einem "echten" Programm wirst du selten die genaue Anzahl deiner Datensätze kennen. Deshalb soll in diesem Beispiel gezeigt werden, wie du mit einer zur Laufzeit definierten Anzahl umgehst.
forumnewbie hat geschrieben:Und dann gleich noch eine Frage zu free(). Wenn ich mit free() eine reservierte Zeiger-Adresse freigebe, dann kann ich danach trotzdem problemlos auf die Daten mittels diesem Zeiger zugreifen. Bedeutet das Wort Freigeben an dieser Stelle, dass die Daten irgendwann überschrieben werden können, aber nicht müssen?
Genau das heißt das. Sie sofort zu überschreiben wäre doch unnötiger Aufwand. Die Daten stören niemanden und wenn man den Speicher neu reserviert wird er sowieso wieder überschrieben. Einzig aus Security-Sicht wäre es annähernd Interessant den Speicher sofort zu überschreiben.
forumnewbie hat geschrieben:Warum darf ich nicht statt:

Code: Alles auswählen

struct Address mainAddress;
folgendes machen bzw. warum funktioniert das nicht?

Code: Alles auswählen

struct Address *mainAddress;
mainAddress = malloc(sizeof(struct Address));
malloc() gibt dir einen Zeiger vom Typ void* zurück. Du musst diesen erst entsprechend auf den von dir erwarteten Typ casten, also nach struct Address*:

Code: Alles auswählen

mainAddress = (struct Address *)malloc(sizeof(struct Address));
Hoffe ich konnte dir damit weiterhelfen :)

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Di Jan 15, 2013 11:05 pm
von forumnewbie
Hallo und vielen Dank für deine Antwort! Ich möchte mich außerdem noch bei den Autoren bedanken, die diese Online-Tutorials für C geschrieben haben/schreiben. Mir haben sie bereits sehr weitergeholfen!

Wenn ich das versuche:

Code: Alles auswählen

    struct Address *mainAddress;
    mainAddress = (struct Address *)malloc(sizeof(struct Address));
erhalte ich einen Fehler "error: expected specifier-qualifier-list before mainAddress;"
Ich verstehe leider diesen Fehler nicht bzw. wie ich diesen beheben kann.

Warum gibt malloc einen void-Zeiger zurück? Auf einer Webseite habe ich gelesen, dass ein void-Zeiger typenlos ist und somit auf jede Adresse zeigen kann - kann aber nicht jeder Zeiger auf jede Adresse im RAM zeigen? Ich dachte, dass malloc einfach die Adresse im Speicher zurückgibt, wo jetzt Platz reserviert wurde. Wofür benötige ich dann verschiedene Datentypen, um dort eine Adresse zu speichern? Ich glaube, dass ich das nicht richtig verstehe.

Danke!

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Di Jan 15, 2013 11:16 pm
von cloidnerux
Warum gibt malloc einen void-Zeiger zurück? Auf einer Webseite habe ich gelesen, dass ein void-Zeiger typenlos ist und somit auf jede Adresse zeigen kann - kann aber nicht jeder Zeiger auf jede Adresse im RAM zeigen?
Hier wirfst du verschiedene Konzepte durcheinender. Ein Pointer kann nicht auf alle Bereiche im Ram zeigen, weil es dann auch auf Bereiche zeigen kann, die du besser nicht erreichen solltest, z.B Systemdateien.
Malloc gibt dir einen void-Pointer zurück, um dich zu Zwingen, es auf deinen Typ anzupassen, damit du dir Gedanken machst, auf welchen Datentyp du deine Pointer zeigen lässt.
erhalte ich einen Fehler "error: expected specifier-qualifier-list before mainAddress;"
Ich verstehe leider diesen Fehler nicht bzw. wie ich diesen beheben kann.
Soweit ich das verstanden habe, ist dien struct noch nicht definiert und dein Compiler weiß nicht was das sein soll.
Ausprobieren.
Wofür benötige ich dann verschiedene Datentypen, um dort eine Adresse zu speichern? Ich glaube, dass ich das nicht richtig verstehe.
Datentypen sind dazu da, dass dein Compiler für dich prüfen kann, dass du sie richtig verwendest. Damit ist sichergstellt, dass du ein text nicht als Zahl behandelst, außer du willst es explizit.
Auch sind einige Operationen von dem Datenytp abhängig.
Wenn du einen Pointer mit "++" inkrementierst, hängt es vom Datentyp ab, um wie viel es inkrementiert wird.

MfG cloidnerux

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Di Jan 15, 2013 11:24 pm
von nufan
forumnewbie hat geschrieben:Wenn ich das versuche:

Code: Alles auswählen

    struct Address *mainAddress;
    mainAddress = (struct Address *)malloc(sizeof(struct Address));
erhalte ich einen Fehler "error: expected specifier-qualifier-list before mainAddress;"
Ich verstehe leider diesen Fehler nicht bzw. wie ich diesen beheben kann.
Hm...? Hast du vielleicht einen Abschreibfehler gemacht oder sonst etwas eingefügt? Hast du eventuell "Address" über ein typedef definiert? Also ich hab folgenden Code:

Code: Alles auswählen

#include <stdlib.h>

struct Address
{
  char           Street[64];
  unsigned int   houseNumber;
  unsigned int   zipCode;
  char           town[64];
};

int main( int argc, char *argv[] )
{
  struct Address *mainAddress;
  mainAddress = (struct Address *)malloc(sizeof(struct Address));
  return 0;
}
Und das kompiliert so:

Code: Alles auswählen

$ gcc -o address address.c -Wall                                                                                                     
address.c: In function ‘main’:                                                                                                                   
address.c:13:19: warning: variable ‘mainAddress’ set but not used [-Wunused-but-set-variable]
Also ohne Fehler (abgesehen von der offensichtlichen aber nicht relevanten Warnung).
forumnewbie hat geschrieben:Warum gibt malloc einen void-Zeiger zurück? Auf einer Webseite habe ich gelesen, dass ein void-Zeiger typenlos ist und somit auf jede Adresse zeigen kann - kann aber nicht jeder Zeiger auf jede Adresse im RAM zeigen? Ich dachte, dass malloc einfach die Adresse im Speicher zurückgibt, wo jetzt Platz reserviert wurde. Wofür benötige ich dann verschiedene Datentypen, um dort eine Adresse zu speichern? Ich glaube, dass ich das nicht richtig verstehe.
void-Zeiger sind typenlos und können wie jeder andere Zeige auf jede beliebige Stelle im RAM zeigen (Achtung: Nur darauf zeigen, für lesen und schreiben gibt es Einschränkungen!).
Warum gibt es verschiedene Typen für Zeiger? Führst du Operationen mit Zeigern durch, wird automatisch die Größe eines einzelnen Elements berücksichtigt. Hier ein kurzes Beispiel:

Code: Alles auswählen

#include <stdio.h>

int main( int argc, char *argv[] )
{
  int *a = (int *)0x1000;
  printf( "%p\n", a );
  a++;
  printf( "%p\n", a );
  return 0;
}
Was würdest du als Ausgabe von diesem Programm erwarten? Die Ausgabe des Programms ist 0x1000 und 0x1004. Du inkrementierst den Zeiger, wodurch er auf das nächste Element zeigt. Das nächste Element liegt aber nicht an der nächsten Speicheradresse 0x1001, sondern genau 4 Byte weiter. Wobei in diesem Fall die 4 Bytes für die Größe eines Elements (also eines ints) stehen. Bei anderen Operationen verhalten sich Zeiger ähnlich. Es wird immer die Größe eines Elements berücksichtigt:

Code: Alles auswählen

#include <stdio.h>

int main( int argc, char *argv[] )
{
  unsigned short int x = 0xFFFF;    // 65535
  printf( "%4X\n", x );
  unsigned char *p = (unsigned char *) &x;
  *p = 0;
  printf( "%4X\n", x );
  return 0;
}
Ausgabe:

Code: Alles auswählen

FFFF
FF00
Das ist eigentlich ein schlechtes Beispiel. Aber du siehst, dass hier nur 1 Byte zugewiesen wird. Der Zeiger soll ja schließlich auch auf ein unsigned char zeigen, das nur 1 Byte groß ist, nicht 2 Byte wie ein unsigned short int.
Du kannst malloc() nicht sagen für welchen Typ du Speicher reservierst. Das ist malloc() auch ziemlich egal. Du verlangst eine Anzahl in Byte und malloc() liefert dir (im Normalfall) einen entsprechenden Speicherbereich. Du bist dafür verantwortlich, dass du den Speicher richtig verwendest. Durch den void-Zeiger wirst du wie von cloidnerux bereits erwähnt dazu gezwungen dir Gedanken über deine Daten zu machen und zu wissen was dein Speicher darstellt.

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Mi Jan 16, 2013 1:10 am
von forumnewbie
Danke für die ausführlichen Antworten!!

Also muss der Zeiger den Datentyp kennen, damit er nicht nur weiß, wo ein Element beginnt, sondern auch wo bzw. wann das Element im Speicher endet? Habe ich das jetzt richtig verstanden? Wenn also ein Zeiger vom Typ Integer ist, dann weiß dieser Zeiger, dass ein Element auf das er zeigt im Arbeitsspeicher 4 Byte groß/lang ist (auf einem 32 bit System). Das war mir absolut nicht klar. Ich dachte, dass der Zeiger sich nur die Startadresse merkt und habe deshalb nicht verstanden, warum er von einem bestimmten Datentyp sein muss.

In meinem Beispiel, was ich aus dem Tutorial genommen habe, sind zwei Strukturen Address und Person. Und Person ist eine erweiterte Address-Struktur mit zusätzlichen Membervariablen.

Und ich wollte folgendes machen bzw. versuchen (siehe Kommentar im Quellcode):

Code: Alles auswählen

##include <stdlib.h>

struct Address
{
    char           Street[64];
    unsigned int   houseNumber;
    unsigned int   zipCode;
    char           town[64];
};

struct Person
{
    char           firstName[64];
    char           familyName[64];

    unsigned int   age;

    struct Address *mainAddress;
    //funktioniert aber nur mit: struct Address secondaryAddress;
    mainAddress = (struct Address *)malloc(sizeof(struct Address));
};

int main()
{
    return 0;
}
Danke für die Hilfe!

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Mi Jan 16, 2013 1:19 am
von nufan
forumnewbie hat geschrieben:Also muss der Zeiger den Datentyp kennen, damit er nicht nur weiß, wo ein Element beginnt, sondern auch wo bzw. wann das Element im Speicher endet? Habe ich das jetzt richtig verstanden? Wenn also ein Zeiger vom Typ Integer ist, dann weiß dieser Zeiger, dass ein Element auf das er zeigt im Arbeitsspeicher 4 Byte groß/lang ist (auf einem 32 bit System). Das war mir absolut nicht klar. Ich dachte, dass der Zeiger sich nur die Startadresse merkt und habe deshalb nicht verstanden, warum er von einem bestimmten Datentyp sein muss.
So ist es.
Nur der Vollständigkeit halber: Mein Beispiel wurde auf einem 64 Bit-System kompiliert und ausgeführt. Mein Integer ist trotzdem 4 Byte groß und nicht 8. Diese Werte geben die Größe der CPU-Register an, aber nicht zwingend jene eines Integers.
forumnewbie hat geschrieben:Und ich wollte folgendes machen bzw. versuchen (siehe Kommentar im Quellcode):

Code: Alles auswählen

#include <stdlib.h>

struct Address
{
    char           Street[64];
    unsigned int   houseNumber;
    unsigned int   zipCode;
    char           town[64];
};

struct Person
{
    char           firstName[64];
    char           familyName[64];

    unsigned int   age;

    struct Address *mainAddress;
    //funktioniert aber nur mit: struct Address secondaryAddress;
    mainAddress = (struct Address *)malloc(sizeof(struct Address));
};

int main()
{
    return 0;
}
Ah... alles klar.
Eine Struktur gibt wie der Name schon sagt nur den Aufbau deiner Daten an. Du kannst damit verschiedene Elemente verschiedener Typen zusammenfassen. Strukturen können aber keinen ausführbaren Code (wie z.B. den Aufruf von malloc()) enthalten! In C geht das allgemein nicht und in C++ nicht auf die Art wie du es versuchst (Die Deklaration von Funktionszeigern innerhalb der Struktur sehe ich jetzt mal nicht als echten "ausführbaren Code"). Du kannst deine Befehle in main oder andere Funktionen schreiben, aber nicht in Strukturen (gleiches gilt für Enums und Unions).

Re: Frage zu einem Beispiel aus dem Tutorial - Struct

Verfasst: Mi Jan 16, 2013 1:34 am
von forumnewbie
Danke! Alle meine Fragen wurden einfach und verständlich beantwortet. :D