Außerdem: Du brauchst in der Aufgabe kein fopen(), du sollst nur von der Standardeingabe lesen!
Tut mir leid, dass ich das mit der Standardeingabe andauernd ignoriert habe, aber jetzt weiss ich, wie es gemeint ist.
Das verstehe ich nicht ganz. Wenn ich erstmal versuchen moechte, nur die erste Zeile einer zu sortierenden Datei mit fgets in die erste "Zeile"meines [m]char **array[/m] zu speichern, wie viel Speicher muss ich fuer das Array vorher allokieren? Wenn ich vorher [m]array = malloc(100*sizeof(char));[/m] schreibe, bleibt der Fehler bestehen, auch wenn ich an die 100 noch ein paar Nullen dranhaenge…
char** array ist im grunde ein array von pointern. Du musst dir zwei Sachen allokieren:
- das array selber
- die einträge des arrays aka die Wörter
Der malloc für char ** array ist also die Anzahl der Wörter * mit der Größe eines char*. Der zweite malloc ist dann die Länge des Wortes * die Größe eines char.
Außerdem beachtest du nicht das eine Zeile die 100 Zeichen lang sein darf beim einlesen ein paar Zeichen länger ist wegen Kontrollzeichen.
Ich habe folgendes Problem:
Ich habe eine char **-Variable deklariert. Folgendermaßen will ich die Größe dynamisch anpassen:
char **wordsToSort = NULL;
.
.
.
if(wordsToSort == NULL){
wordsToSort = malloc( wordsInLine * sizeof(char *));
if(wordsToSort == NULL){
perror("malloc");
exit(EXIT_FAILURE);
}
} else {
wordsToSort = realloc(wordsToSort, (size_t)(sizeof(wordsToSort)+wordsInLine*sizeof(char *)));
if(wordsToSort == NULL){
perror("realloc");
exit(EXIT_FAILURE);
}
}
In “wordsInLine” steht die Anzahl der Wörter, die in der aktuellen Zeile stehen.
Bei der “einfachsten” Beispiel-Datei steht in jeder Zeile ein Wort. Das heißt, beim Aufruf von realloc kommt jeweils immer die alte Größe + sizeof(char *) raus.
Nach dem Aufruf von realloc ändert sich die Größe des char **-Arrays aber nicht.
Kann mir da jemand weiterhelfen?
Edit: Habe es auch wie auf den Übungsfolien probiert und eine char **newArea-Variable angelegt und dieser zunächst das Ergebnis von realloc zugewiesen.
Und dann anschließend wordsToSort = newArea zugewiesen, Auch das brachte keinen Erfolg.
Vielen Dank!
Problem: sizeof(wordsToSort) = sizeof(char **) = 8 (auf einem 64-bit System).
Du denkst dabei aber wahrscheinlich an so etwas wie (Java) array.length, das geht aber in C (für dynamisch allokierten Speicher) nicht.
Du musst dir also was anderes überlegen, um die Anzahl der bisherigen Wörter zu kennen.
Und ja, du solltest für den Fehlerfall in eine extra Variable allokieren, um den originalen Speicher im Fehlerfall auch wieder freigeben zu können.
Danke schonmal, ich habe es jetzt geschafft, wlist0 in einem Array zu speichern. Beim Uebersetzen mit den fuer die Abgage erforderlichen Flags kommt auch keine Fehlermeldung.
Wenn ich jedoch wlist1 in einem Array speichern moechte, kommt in der Console die Fehlermeldung: [m]*** glibc detected *** ./wsort: malloc(): memory corruption: 0x000000000200da20 ***[/m] und das Programm terminiert nicht.
Also hab ich [m]valgrind --leak-check=full --show-reachable=yes ./wsort[/m] eingegeben, um den Uebeltaeter zu finden, aber das terminiert auch nicht und gibt mir ausser dem Folgenden nichts aus:
[m]==4290== Memcheck, a memory error detector
==4290== Copyright (C) 2002-2011, and GNU GPL’d, by Julian Seward et al.
==4290== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==4290== Command: ./wsort
==4290==[/m]
Darunter blinkt nur der Cursor weiter.
Wenn ich es mit CTRL+C beende, dann kommt das hier:
[m]^C==4290==
==4290== HEAP SUMMARY:
==4290== in use at exit: 105 bytes in 1 blocks
==4290== total heap usage: 1 allocs, 0 frees, 105 bytes allocated
==4290==
==4290== 105 bytes in 1 blocks are still reachable in loss record 1 of 1
==4290== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4290== by 0x4007D0: main (wsort.c:32)
==4290==
==4290== LEAK SUMMARY:
==4290== definitely lost: 0 bytes in 0 blocks
==4290== indirectly lost: 0 bytes in 0 blocks
==4290== possibly lost: 0 bytes in 0 blocks
==4290== still reachable: 105 bytes in 1 blocks
==4290== suppressed: 0 bytes in 0 blocks
==4290==
==4290== For counts of detected and suppressed errors, rerun with: -v
==4290== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)[/m]
Ich bin wirklich ratlos, wie ich den Fehler finden soll, da ich nicht einmal weiss, wonach ich suchen soll. Die einzige Zeile aus meinem Code, die mir valgrind anzeigt, ist die Zeile 32, in der ich einer Variable Speicher zuweise, aber am Ende vom Programm mit free(…) wieder freigebe.
Habe folgendes problem:
ich übergebe meiner einlese funktion mein zweidimensionales char array, dieses fülle ich dann mit den wörtern.
dabei wird mehrmals neuer speicher über realloc angefordert. anschließend sind auch alle wörter in dem array.
wenn ich nun wieder in der main bin sind allerdings alle zeiger auf meine wörter die ab dem neu (über realloc) allocierten speicher liegen, wieder null.
verstehe nicht ganz wie es dazu kommt?
Machst du sowas wie das?
int main() {
char **arr = malloc(...);
for (;;) readword(arr);
}
void readwort(char **arr) {
arr = realloc(arr, ...);
arr[...] = ...;
}
Falls ja, musst du dafür sorgen, dass der Pointer, der aus [m]realloc[/m] rausfällt, zurückgegeben wird und in deiner [m]main[/m] dann weiterverwendet wird, denn dabei wird ein neuer Speicherbereich allokiert, in diesen werden die Daten dann kopiert und der alte wieder freigegeben, der neue Speicher liegt folglich auch an einer anderen Adresse. Das, worauf [m]arr[/m] in der [m]main[/m] bei dem Code nach einem Aufruf von [m]readword[/m] zeigt, ist Speicher, der gefreet wurde (außer bei [m]realloc[/m] ist ein Fehler aufgetreten), den solltest du dann auf keinen Fall verwenden.
genau das war das problem, das einlesen funktioniert jetzt schonmal! danke izibi!!
Wir haben jetzt eine lauffähige Version.
Beim Vergleich mit den Dateien vom Lehrstuhl passen wlist0,1,2,6.
Bei wlist3,4,5 kommt als “Unterschied” zwischen den Dateien so etwas wie:
diff wlist5.mine wlist5.spteam
1,9d0
<
<
<
<
<
<
<
<
<
Aber solche Zeichen finde ich in der Datei gar nicht.
Hat das Problem sonst noch jemand?
Vielen Dank!
Zeilen die mit “<” beginnen sind nur in der ersten Datei vorhanden (in diesem Fall also in deiner Version), dort aber ohne “<”. Das “1,9d0” ist AFAIK einfach die Adresse des Unterschieds. Sieht so aus als hättet ihr da “leere” Zeilen (‘\n’) oder vielleicht Zeilen mit einem Leerzeichen.
Nutzt doch besser [m]diff -u[/m]. Da wird man zumindest beim Lesen der Ausgabe nicht wahnsinnig.
Vielen Dank!
Die Adresse am Anfang hat mich verwirrt. Waren wirklich einfach leere Zeilen am Anfang unserer Datei.
Aber beim Öffnen in gedit habe ich die irgendwie nicht gesehen. Komisch, aber hat sich ja jetzt geklärt
Ich bin jetzt soweit, dass ich die wlist*-Dateien in ein Array speichern und die Listen auch sortieren kann. Jetzt fehlt nur noch, die Einschraenkung mit den 100 Zeichen und den leeren Zeilen zu beachten. Dazu habe ich jetzt eine Frage:
Wenn ich [m]fgets(tmp, 104, zeichenkette) [/m] ausfuehre, wobei [m]tmp[/m] und [m]zeichenkette[/m] [m]char * [/m] - Variablen sind und [m]zeichenkette[/m] aus z.B. 300 chars besteht. Dann werden in [m]tmp[/m] die ersten 104 Zeichen von [m]zeichenkette[/m] gepseichert und beim naechsten Ausfuehren von [m]fgets(tmp, 104, zeichenkette) [/m] werden die Zeichen Nr. 105 bis 209 in [m]tmp[/m] gespeichert.
Wie kann ich es schaffen, dass man die komplette Zeile (hier zeichenkette) verwirft und [m]fgets(…)[/m] beim naechsten Aufruf mit der naechsten Zeile beginnt?
Dafür musst du selbst so lange weiter einlesen, bis du das Ende der Zeile erreicht hast.
[size=8]Warum hier nicht einfach [m]getline(3)[/m] verwendet wird, werde ich wohl nie verstehen. Ist schmerzfreier, man hat keine Beschränkung der Zeilenlänge und ist mittlerweile auch POSIX-konform.[/size]
Bei dieser Umsetzung liegt jedoch mein Problem. Ein Beispiel (ohne auf den Speicher zu achten):
Hier sei die maximal erlaubte Zeilenlaenge 8.
Man uebergibt ueber stdin eine Datei, in der in der ersten Zeile “123456789_123456789_123456789” und in der zweiten Zeile “zweite Zeile” steht.
char *tmp;
fgets(tmp, 9, stdin);
fgets(tmp, 9, stdin);
fgets(tmp, 9, stdin);
fgets(tmp, 9, stdin);
Danach steht in tmp “56789”. Woher weiss ich nun, ob das, wie in diesem Fall, nur der letzte Rest der Zeile oder schon die neue Zeile ist?
Die einzige Moeglichkeit, die mir einfaellt, um das zu ueberprufen, waere, nach jedem fgets()-Aufruf zu schauen, ob in tmp ein ‘\n’ am Ende steht, aber das wuerde doch viel Rechenzeit kosten, wenn man das bei ueber einer millionen Zeilen macht?
Es kostet doch nicht viel Rechenzeit einen Character zu überprüfen. Du vergisst außerdem, dass fgets(3) immer ein
‚\0‘ an jedes Ende von dem eingelesenen String packt. Also müsstest du immer 9 Zeichen einlesen.
Dann kannste mit strlen(3) überprüfen wie lang das Wort ist und dann ganz schnell testen ob eine Newline am Ende ist …
habe auch die leerzeilen am anfang, wie kamen die bei euch zu stande?
Hast du vielleicht nicht bedacht, dass du leere Zeilen rausschmeißen musst?
@m0: wir hatten nicht berücksichtigt, dass zwischen Wörtern mehr als 1 Leerzeichen sein kann.
hier ein mini Testfile zum sortieren:
begin file
HAUSHERRN EinLeer ZweiLeer DreiLeer
testLeerAtBeginning
test 20Leer
darueberWarLeerEmpty
REALLYREALLYlonglonglonglonglongWord
end file
wichtig: in Zeile 5 (die über “darueberWarLeerEmpty”) ist auch ein Leerzeichen