kann mir jemand das erklären?

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

kann mir jemand das erklären?
das hier hab ich im Vorjahresjahrgangsforum gefunden:

int anzahl = 0;
char *pointer = malloc(sizeof(char)*101);
while (fgets(pointer+(anzahl++*101), 101, stdin) != NULL) { pointer = realloc(pointer, sizeof(char)*(anzahl+1)*101); 
qsort (pointer, anzahl, 101, (int (*) (const void *, const void *)) &strcmp);

pointer is doch array. aber wie kann ich ein array initialisieren ohne anzugeben, wie viele Felder es hat? sicher hat das was mit dem alloc und realloc zu tun… bloß was?

ist das obige als ‘böser’ oder ‘guter’ code einzustufen?
bin ich eigentlich der einzige, der solche Probleme hat???


ja, es HAT was damit zu tun. im prinzip kannst du jeden pointer als array verwenden und umgekehrt: wenn du ein array definierst, hast du gleichzeitig einen pointer auf das erste element.

beispiel:

int array [5] = {1, 2, 3, 4, 5};

ist gleichwertig mit

int *array;
array = (char *) malloc (5 * sizeof (char));
// es werden 5 bytes allokiert, der pointer auf den anfang wird array zugewiesen
// jetzt kann auf den speicher wie bei einem array zugegriffen werden
// z. b.
for (i = 0; i < 5; i++) {
    array [i] = i + 1;
}

ein unterschied besteht z. b. darin, dass du im 2. fall dynamisch zur laufzeit speicher allokierst und dich nicht schon vorher auf 5 elemente festlegst. veraendern kannst du das ganze nochmal mit realloc.

zum anderen code:
er ist SEHR kurz. das ist erstmal ganz gut, denke ich, allerdings fehlt halt jegliche fehlerbehandlung und das problem mit dem ueberfuellten buffer ist auch nicht geloest…


:slight_smile:

ein Zeiger IST ein Array … im Prinzip jedenfalls …

Was dieser Code macht:

int anzahl = 0;
→ Eine Zählervariable initialisieren

char *pointer = malloc(sizeof(char)*101);
→ Einen Zeiger basteln, der auf 101 Bytes (sofern auf einem System auf dem ein Char nur ein Byte groß ist kompiliiert) zeigt

while (fgets(pointer+(anzahl++101), 101, stdin) != NULL) { pointer = realloc(pointer, sizeof(char)(anzahl+1)101);
→ Pfrimeln wir das mal auseinander: Das Abbruchkriterium der Schleife ist also, wenn fgets NULL zurückgibt. Der fgets-Ausdruck selbst liest 101 Zeichen von der Standardeingabe und schreibt sie an die Zählervariable
101. Stelle. Bedeutet also beim 1. Durchlauf an die 0. Stelle, was der Anfang dieses Zählers ist. So, wenn das Einlesen geklappt hat, braucht man ja wieder Speicher für die nächsten 101 Byte, ergo: realloc. Und die nächsten 101 Zeichen werden eingelesen, usw.
Was man dabei am Ende hat, ist ein großer, zusammenhängender Speicherbereich, bei dem alle 101 Zeichen/Byte ein neues Wort anfängt.

qsort (pointer, anzahl, 101, (int (*) (const void *, const void *)) &strcmp);
→ Ruft einfach nur qsort auf und sagt „ihm“, dass eben anzahl Wörter der länge 101 (strcmp wird nicht die vollen 101 chars vergleichen, sondern bei \n aufhören) verglichen werden sollen. Der Aufruf von strcmp ist auch ok … wollte das letztes Jahr auch so machen, aber da war noch eine „Extra-Funktion“ verlangt … aber wozu einen Wrapper für strcmp schreiben, wenn man es auch direkt verwenden kann :slight_smile:

Um also deine Fragen zu beantworten:

  1. Warum sollte man bei einem Array vorher angeben wieviele Felder er hat, besonders dann, wenn man das nicht vorher wissen kann?
  2. der Code ist „böser“ Code, weil er keine Fehlerbehandlungen enthält. Außerdem frisst er Speicher, da für jedes Wort 101 Bytes reserviert werden.

Grüße,
Sebbi


Also ein

char array[10];

ist im Grunde genau das gleiche wie

char *array_pointer;
array_pointer = malloc(sizeof (char) * 10);

Ein Array unter C ist im Grunde auch nur ein Haufen Speicher. Mit den eckigen Klammern dahinter wird die Größe angegeben, die der Compiler reservieren soll. Der Bezeichner array[x] (wobei x ne Zahl größer Null ist) zeigt bei array[0] auf das erste Array Element, genau wie der Pointer array_pointer. array[0] und array_pointer zeigen also beide auf die erste Stelle.
array[1] und array_pointer = array_pointer + 1 zeigen auf die zweite Stelle im Array.
array ist, ohne die eckigen Klammern benutzt, auch nur der Zeiger aufs erste Element des Speicherbereichs.

char *array_pointer;
array_pointer = malloc(sizeof (char) * 10);
array_pointer[2] ='h';

geht genauso wie

char array[10];
array[2] = 'h';

nicht gehen tut allerdings

char array[10];
array++;

weil dann die [] adressierung vom statisch angelegtem Array schief gehen würde, weil ja dann der Anfang um eine Position verschoben wurde.

Ein beliebter Fehler ist, zu wenig Speicher zuzuweisen und dann über die Array Grenzen zu schreiben.

char array[10];
array[20] = 'h';

sollte an sich kompilieren - allerdings wird dann anderer vielleicht benötigter Speicher verändert. Dann kann es an ganz anderen Stellen auf einmal abstürzen, oder man kommt über den Speicherbereich des Programmes hinaus(Segment) und bekommt einen Segmentation fault.

@Max Das Programm von an sich sieht ok aus, nur fehlen beim Malloc als auch beim Realloc die Fehlerabfragen. Das ist wichtig und darauf legen die Übungsmenschen wert!
Du kannst auch char array[]; schreiben - das ist aver das gleiche wie char *array; In beiden Fällen muss dann ein malloc danach kommen.


ah wunderbar :cheesy:
ihr seid klasse :wink:

jetzt kapier sogar ichs :wink:


falls noch nicht durchgedrungen ist, wieso die initialisierung auch in der laufzeit stattfinden kann, das liegt schlichtweg daran, dass sich der vom pointer selbst belegte speicher dadurch nicht ändert.