Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]13058[/thread]

C, malloc, realloc, int arrays und segfaults

Leser: 15


<< >> 7 Einträge, 1 Seite
FoolAck
 2009-01-27 05:24
#118371 #118371
User since
2008-05-02
69 Artikel
BenutzerIn
[default_avatar]
Hallo.
Erstmal sorry für den komischen Titel, aber ich weiß halt wirklich nicht worans hier liegt...
Ich lese eine Datei char für char in ein int-Array (ja, ich weiß, 3 Bytes verschenkt pro int) und lasse dieses bei Bedarf anwachsen.
Es segfaulted jedoch ab einer bestimmten Größe (/array Position) immer.
Hier ein so gut wie möglich gekürztes Beispiel:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>

int *getinp(const char *filename);


int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(EXIT_FAILURE);
}

int *foo = getinp(argv[1]);
return 0;
}

int *getinp(const char *filename) {
FILE *fd = fopen(filename, "r");
if (fd == NULL) {
fprintf(stderr, "Couldn't open %s for reading\n", filename);
exit(EXIT_FAILURE);
}

int pos = 0;
int size = 64;
int *arr = malloc(size * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "Not enough memory\n");
exit(EXIT_FAILURE);
}
char c;

while ((c = fgetc(fd)) != EOF) {
arr[pos] = c;
pos++;
if (pos >= (size - 1)) {
size *= 2;
if (realloc(arr, size * sizeof(int)) == NULL) {
fprintf(stderr, "Not enough memory\n");
exit(EXIT_FAILURE);
}
}
}
arr[pos] = '\0';
fclose(fd);
return arr;
}



In gdb:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ gdb --args ./a.out big.bad.testfile
[gdb]> run
Starting program: /home/user/a.out big.bad.testfile

Program received signal SIGSEGV, Segmentation fault.
0x08048646 in getinp (filename=0xbf9c75b1 "big.bad.testfile") at fail.c:34
34 arr[pos] = c;
[gdb]> p size
$1 = 65536
[gdb]> p pos
$2 = 33700
[gdb]> p arr[pos-1]
$3 = 9
[gdb]> p arr[pos]
Cannot access memory at address 0x806b000


pos ist 33700, size ist 65536, arr[pos-1] ist lesbar, arr[pos] aber nicht. Dies ist zu 100% reproduzierbar (bei meinen Versuchen zumindest). Woran liegts? Irgendwie seh ich den Fehler nicht.
Ich meine, wenn malloc() oder realloc() fehlschlagen würden, würde das Script abbrechen und schreien. Tut es aber nicht. Laut der Logik sollte der Speicher eigentlich für das Array reserviert sein.
Ideen?
esskar
 2009-01-27 08:59
#118374 #118374
User since
2003-08-04
7321 Artikel
ModeratorIn

user image
änder mal folgende zeilen

Code: (dl )
1
2
3
4
5
size *= 2;
if (realloc(arr, size * sizeof(int)) == NULL) {
fprintf(stderr, "Not enough memory\n");
exit(EXIT_FAILURE);
}


in

Code: (dl )
1
2
3
4
5
6
size *= 2;
arr = realloc(arr, size * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "Not enough memory\n");
exit(EXIT_FAILURE);
}

size *= 2; wird schnell groß, dass ist dir klar, oder? würde es auch fast so schreiben size += size;

HTH
FoolAck
 2009-01-27 09:29
#118376 #118376
User since
2008-05-02
69 Artikel
BenutzerIn
[default_avatar]
Jo danke esskar. Hat geholfen.
War mir irgendwie klar, dass es ein vermeidbarer (irgendwo offensichtlicher) Fehler sein musste.

Hab das hier wohl über-/nicht genau genug gelesen:
Quote
void *realloc(void *ptr, size_t size);
[...]
On success a pointer to the memory block is returned (which may be in a different location as before). On failure or if size is zero, a null pointer is returned.

Ich dachte man braucht den return value nur für error-checking.
Aber stimmt, wenn der angeforderte memory-Block zu groß ist muss er die Startaddresse (bzw. den gesamten Block) wohl verschieben.

Nunja. Nochmals danke für die fixe Hilfe.

edit:
Quote
size *= 2; wird schnell groß, dass ist dir klar, oder? würde es auch fast so schreiben size += size;

Das das rechnerisch das Gleiche ist, ist dir klar, oder? Oder meinst du vonwegen "Stil"?
Hm, aber dadurch, dass die Größe quadratisch ansteigt kommt man mit sehr wenigen realloc() Aufrufen aus. Wobei natürlich viel an Speicher verschenkt wird. Hm, aber ich denke ich kann damit leben.
moritz
 2009-01-27 10:27
#118382 #118382
User since
2007-05-11
923 Artikel
HausmeisterIn
[Homepage]
user image
Wenn ich segfaults oder memory leaks debugge, ist manchmal `valgrind' sehr nuetzlich, das gibt interessante Debugginginformationen aus.
esskar
 2009-01-27 12:03
#118387 #118387
User since
2003-08-04
7321 Artikel
ModeratorIn

user image
FoolAck+2009-01-27 08:29:25--
Das das rechnerisch das Gleiche ist, ist dir klar, oder?


beim einen ist es addition, beim andern multiplikation :-)

murphy
 2009-01-27 17:10
#118402 #118402
User since
2004-07-19
1776 Artikel
HausmeisterIn
[Homepage]
user image
esskar+2009-01-27 07:59:51--
size *= 2; wird schnell groß, dass ist dir klar, oder? würde es auch fast so schreiben size += size;


Es ist aber sinnvoll, den Speicherpuffer exponentiell zu vergroessern, um die Anzahl der Allokationen und die Fragmentierung des Speichers moeglichst gering zu halten. Fast jede Implementation dynamisch wachsender Arrays arbeitet so.

size += size zu schreiben halte ich fuer wenig sinnvoll. Wenn man hier unbedingt von Hand optimieren und sich nicht darauf verlassen will, dass der Compiler sowieso diesen Code erzeugt, dann sollte man wohl eher size <<= 1 schreiben, was mit Sicherheit eine schnellere Instruktion ist als die Addition oder Multiplikation.

Noch besser als einen dynamisch wachsenden Puffer zu verwenden waere es allerdings, einfach die Groesse der einzulesenden Datei zu bestimmen und direkt einen Puffer passender Groesse zu allozieren.

Wenn ferner eine Konversion der Daten in der Datei in eine spezielle Repreasentation im Arbeitsspeicher unnoetig ist -- was hier der Fall zu sein scheint, denn die Konversion von Bytes in Words ist voellig ueberfluessig -- dann empfiehlt es sich, die Datei einfach in den Speicher zu mappen anstatt sie einzulesen.
When C++ is your hammer, every problem looks like your thumb.
esskar
 2009-01-27 22:14
#118404 #118404
User since
2003-08-04
7321 Artikel
ModeratorIn

user image
murphy+2009-01-27 16:10:41--
Es ist aber sinnvoll, den Speicherpuffer exponentiell zu vergroessern, um die Anzahl der Allokationen und die Fragmentierung des Speichers moeglichst gering zu halten. Fast jede Implementation dynamisch wachsender Arrays arbeitet so.

ja, aber ich würde da nicht mit 64 bytes anfangen. Buffers unter 16K sind witzlos.
<< >> 7 Einträge, 1 Seite



View all threads created 2009-01-27 05:24.