(C / Linux) Richtig debuggen?
*Markus
- sonstiges
Hallo,
Hintergrund: Mein Programm stürzt in manchen Fällen ab und in manchen nicht, v.a. stürzt es fast immer ab, wenn ich mein Home-Verzeichnis einlese. Ich konnte den Fehler eingrenzen um zu wissen, wo er passiert, aber ich kann mir bisher nicht erklären warum gerade hier. Hier mal die Funktion in der es passiert:
File* readDirectory(char* dirname) {
DIR *dir;
struct dirent *dp; // wird von readdir() zurückgeliefert
size_t filecount = 0; // number of entries in directory
size_t i = 0;
File* files;
size_t file_len, dir_len; //Wird für die Konkatenation des Filenames und des Dirs benötigt
struct stat s; //enthält alle Infos zu einem File
if ( (dir = opendir(dirname)) == NULL)
return NULL;
while ((dp = readdir(dir)) != NULL)
filecount++;
files = (File *)calloc(filecount, sizeof(File *)); //Genauso viel Speicher reservieren wie man File-Zeiger hat
if (!files) //Für jede Datei wird hierbei ein neues File angelegt
return NULL;
rewinddir(dir);
while (((dp = readdir(dir)) != NULL)) {
file_len = strlen(dp->d_name); //Verzeichnispfad konkatenieren
dir_len = strlen(dirname);
char newpath[file_len+dir_len+2]; //+2 weil \0 und / zw. dir- und filename
strcpy(newpath, dirname);
strcat(newpath, "/");
strcat(newpath, dp->d_name);
//Konkattenieren Ende
stat(newpath, &s); //get file status
//Neues File alloziieren
if ( (files[i] = (File)malloc(sizeof(File)) ) == NULL) {
while (i > 0)
free(files[--i]);
free(files);
fprintf(stderr, "Insufficient memory for file allocation!");
return NULL;
}
files[i]->inode_number = s.st_ino;
printf("inode %ld\n", files[i]->inode_number);
files[i]->filename = (char *)strdup(dp->d_name);
printf("filename %s\n", files[i]->filename);
files[i]->lastedit = (char *)strdup(ctime(&s.st_mtime));
printf("lastedit %s\n", files[i]->lastedit);
files[i]->filesize = s.st_size;
printf("filesize %ld\n", files[i]->filesize);
files[i]->filetype = (char *)strdup(getFileType(dp->d_type)); //den Filetype übergeben und einen entsprechenden String zurückliefern
printf("filetype %s\n", files[i]->filetype);
files[i]->owner = s.st_uid;
printf("owner %d\n", files[i]->owner);
files[i]->group = s.st_gid;
printf("group %d\n", files[i]->group);
// (*files[i])->rating =
// files[i]->path = (char *)strdup(newpath);
// printf("path %s\n", files[i]->path);
// (*files[i])->icon =
// (*files[i]->metadata =
i++;
}
printf("dir closed\n");
closedir(dir);
return files;
}
Wie man sieht, ließ ich mir sämtliche Inhalte ausgeben um den Fehler weiter eingrenzen zu können. Ein Crash passiert ab dieser Zeile files[i]->group = s.st_gid; Davor konnte ich bisher keine Crashs reproduzieren. Offensichtlich gibt es irgendwann Schwierigkeiten, die Gruppe einzulesen, oder was auch immer. Auf != NULL kann ich nicht überprüfen, da gcc mit einer Warnung meckert, ich würde zwischen einem Pointer und unsigned int vergleichen. Mit einem "if (s.st_gid)" kann ich die Crashs ebenso nicht abfangen. Die Struktur dazu sieht im Übrigen so aus:
struct file {
unsigned long inode_number;
char* filename;
char* lastedit;
unsigned long filesize;
char* filetype;
unsigned int owner;
unsigned int group;
unsigned int rating;
char* path;
char* icon;
char** metadata; //Array von Metadaten
};
Ironischerweise stürzt das Programm bei owner nicht ab, aber bei group. Den Unterschied verstehe ich bisher zwar nicht aber gut. Zu diesem Zweck wollte mir angewöhnen mit gdb zu arbeiten. Dummerweise bekomme ich keine vernünftige Ausgabe, da mir irgendwelche debugging symbols fehlen. Ich weiß, das man da irgendwelche Files benötigt aber wie weiß welche das sind und wo bekomme ich die her? Die Ausgabe sieht so aus:
markus@archy ~/C-Programme/Projekte/Dateibrowser $ gdb BrowserGUI GNU gdb 6.8 Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"... (no debugging symbols found) (gdb) run Starting program: /home/markus/C-Programme/Projekte/Dateibrowser/BrowserGUI (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) [Thread debugging using libthread_db enabled] Error while reading shared library symbols: Cannot find new threads: generic error Cannot find new threads: generic error (gdb) bt #0 0xb80d29b1 in _dl_debug_state () from /lib/ld-linux.so.2 #1 0xb80d611c in dl_open_worker () from /lib/ld-linux.so.2 #2 0xb80d1ce6 in _dl_catch_error () from /lib/ld-linux.so.2 #3 0xb80d5a70 in _dl_open () from /lib/ld-linux.so.2 #4 0xb7659beb in dlopen_doit () from /lib/libdl.so.2 #5 0xb80d1ce6 in _dl_catch_error () from /lib/ld-linux.so.2 #6 0xb765a07c in _dlerror_run () from /lib/libdl.so.2 #7 0xb7659b21 in dlopen@@GLIBC_2.1 () from /lib/libdl.so.2 #8 0xb7a006c4 in g_module_open () from /usr/lib/libgmodule-2.0.so.0 #9 0xb7e4718c in load_modules () from /usr/lib/libgtk-x11-2.0.so.0 #10 0xb7e473e5 in _gtk_modules_settings_changed () from /usr/lib/libgtk-x11-2.0.so.0 #11 0xb7e474e1 in display_opened_cb () from /usr/lib/libgtk-x11-2.0.so.0 #12 0xb7a19aa8 in g_cclosure_marshal_VOID__OBJECT () from /usr/lib/libgobject-2.0.so.0 #13 0xb7a0cd42 in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0 #14 0xb7a20fe8 in signal_emit_unlocked_R () from /usr/lib/libgobject-2.0.so.0 #15 0xb7a2235d in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0 #16 0xb7a22657 in g_signal_emit_by_name () from /usr/lib/libgobject-2.0.so.0 #17 0xb7cb0671 in gdk_display_open () from /usr/lib/libgdk-x11-2.0.so.0 #18 0xb7c89ba5 in gdk_display_open_default_libgtk_only () from /usr/lib/libgdk-x11-2.0.so.0 #19 0xb7e2ccf1 in gtk_init_check () from /usr/lib/libgtk-x11-2.0.so.0 #20 0xb7e2cd24 in gtk_init () from /usr/lib/libgtk-x11-2.0.so.0 #21 0x08049925 in initMainWindow () #22 0x080496a0 in main () (gdb) quit
Selbst mit strace komme ich nicht weiter. Das sind die letzten Zeilen von strace:
open("/home/markus", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 4 getdents(4, /* 209 entries /, 32768) = 4736 getdents(4, / 45 entries /, 32768) = 1260 getdents(4, / 0 entries /, 32768) = 0 lseek(4, 0, SEEK_SET) = 0 getdents(4, / 209 entries */, 32768) = 4736 stat64("/home/markus/.", {st_mode=S_IFDIR|0755, st_size=8192, ...}) = 0 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7094000 write(1, "inode 2321985\n"..., 14inode 2321985 ) = 14 write(1, "filename .\n"..., 11filename . ) = 11 open("/etc/localtime", O_RDONLY) = 5 fstat64(5, {st_mode=S_IFREG|0644, st_size=2211, ...}) = 0 fstat64(5, {st_mode=S_IFREG|0644, st_size=2211, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7093000 read(5, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\6\0\0\0\0\0"..., 4096) = 2211 _llseek(5, -28, [2183], SEEK_CUR) = 0 read(5, "\nCET-1CEST,M3.5.0,M10.5.0/3\n"..., 4096) = 28 close(5) = 0 munmap(0xb7093000, 4096) = 0 write(1, "lastedit Sun Oct 11 08:33:47 2009"..., 34lastedit Sun Oct 11 08:33:47 2009 ) = 34 write(1, "\n"..., 1 ) = 1 write(1, "filesize 8192\n"..., 14filesize 8192 ) = 14 write(1, "filetype File\n"..., 14filetype File ) = 14 write(1, "owner 1000\n"..., 11owner 1000 ) = 11 write(1, "group 101\n"..., 10group 101 ) = 10 stat64("/home/markus/..", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 write(1, "inode 2\n"..., 8inode 2 ) = 8 write(1, "filename ..\n"..., 12filename .. ) = 12 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2211, ...}) = 0 write(1, "lastedit Thu Jan 15 15:45:35 2009"..., 34lastedit Thu Jan 15 15:45:35 2009 ) = 34 write(1, "\n"..., 1 ) = 1 write(1, "filesize 4096\n"..., 14filesize 4096 ) = 14 write(1, "filetype File\n"..., 14filetype File ) = 14 write(1, "owner 0\n"..., 8owner 0 ) = 8 write(1, "group 0\n"..., 8group 0 ) = 8 stat64("/home/markus/.java", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 write(1, "inode 3237876\n"..., 14inode 3237876 ) = 14 write(1, "filename .java\n"..., 15filename .java ) = 15 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2211, ...}) = 0 write(1, "lastedit Thu Nov 27 22:39:32 2008"..., 34lastedit Thu Nov 27 22:39:32 2008 ) = 34 write(1, "\n"..., 1 ) = 1 write(1, "filesize 4096\n"..., 14filesize 4096 ) = 14 write(1, "filetype File\n"..., 14filetype File ) = 14 write(1, "owner 1000\n"..., 11owner 1000 ) = 11 write(1, "group 101\n"..., 10group 101 ) = 10 stat64("/home/markus/C-Programme", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 write(1, "inode 2322006\n"..., 14inode 2322006 ) = 14 write(1, "filename C-Programme\n"..., 21filename C-Programme ) = 21 stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2211, ...}) = 0 --- SIGSEGV (Segmentation fault) @ 0 (0) --- +++ killed by SIGSEGV +++
Meine Frage lautet: Wie debugge ich richtig?
Danke. Viele Grüße, Markus
Hallo Markus,
files = (File *)calloc(filecount, sizeof(File *)); //Genauso viel Speicher reservieren wie man File-Zeiger hat
Zum einen: Hier wäre es wieder interessant zu erfahren, wie File definiert ist. Ist das ein typedef auf struct file oder auf struct file *?
Zum anderen: Das ist falsch! Du willst ein Array allozieren (d.h. files ist om Typ "File *"), jedes einzelne Element (2. Argument von calloc) ist aber vom Typ "File", d.h. es hat die Größe sizeof(File), nicht sizeof(File *)! In Deinem anderen Thread hattest Du das zumindest doch richtig?
if ( (files[i] = (File)malloc(sizeof(File)) ) == NULL) {
Ok, das lässt mich darauf schließen, dass Du
typedef struct file *File;
hast. Ich habe aus gutem Grund davon abgeraten! Denn hier hast Du das Problem: sizeof(File) ist die Größe eines Zeigers (!) auf die Struktur, nicht die Größe der Struktur! Du allozierst hier also mit Abstand viel zu wenig (!) für jede einzelne Datei!
Ich füge hier nochmal ein paar generelle Erklärungen zu Zeigern in C und zu malloc() / calloc() ein:
1. Ein Zeiger ist eine Variable, die die Adresse eines Speicherbereichs enthält. Beispiel:
int a = 0;
int *p = &a;
Dann sieht der Speicher so aus:
+------------------+
a: | 0 |<-----+
+------------------+ |
p: | 0x......... ----|------+
+------------------+
D.h. p ist eine Variable, die die Speicheradresse der Variable a enthält. a enthält dagegen den Wert der Variable.
sizeof(p) gibt Dir die Größe der Variable p zurück, d.h. die benötigten Bytes, um eine Speicheradresse (!) zu speichern. sizeof(a) gibt Dir die Größe der Variable a zurück, d.h. die benötigten Bytes, um den Wert (!) zu speichern.
2. malloc/calloc
malloc/calloc liefern Dir einen Zeiger auf einen dynamisch allozierten Speicherbereich zurück. Dieser Speicherbereich hat die Größe, die Du malloc/calloc übergibst, malloc/calloc geben Dir jedoch nur die Adresse zurück, aus der Du nicht die Größe des Speicherbereichs sclhießen kannst. Beispiel:
int *p = (int *)malloc(1); /* BÖSE */
+----------------+ +----------------------------------+
p: | 0x........ -|------>| Speicherbereich der Größe 1 Byte |
+----------------+ +----------------------------------+
Das würde einen Speicherbereich der Größe 1 Byte allozieren und die Adresse zurückgeben. Da passt natürlich kein int hinein, d.h. wenn Du auf *p zugreifen willst, hast Du ein Problem.
Weiteres Beispiel:
int *p = (int *)malloc(sizeof(int)); /* OK */
+----------------+ +---------------------------------------+
p: | 0x........ -|------>| Speicherbereich der Größe sizeof(int) |
+----------------+ +---------------------------------------+
Hier allozierst Du einen Speicherbereich, wo ein int hineinpasst und weist die Speicheradresse der Variablen p zu. Nachdem in den Speicherbereich, auf den p zeigt, genügend hineinpasst, hast Du hier keine Probleme.
3. Dynamische Arrays
In C kannst Du auf Zeiger wie auf Arrays zugreifen. Im Endeffekt ist die Syntax array[i] nichts anderes als eine Kurzform für *(array + i), d.h. nimm die Speicheradresse von "array" und addiere i mal die Elementgröße des Typs auf den Array zeigt drauf und dereferenziere diese neue Speicheradresse. Beispiel:
int *p = (int *)calloc(4, sizeof(int));
p[0] = 1;
p[1] = 2;
p[2] = 3;
p[3] = 4;
printf ("%d\n", *(p + 2));
Ausgabe: 3.
Im Speicher sieht das ganze so aus:
jedes dieser Elemente ist
von der Größe sizeof(int)
_____ _____ _____ _____
/ \ / \ / \ / \
+----------+ +-------+-------+-------+-------+
p: | 0x..... -|----------->| 1 | 2 | 3 | 4 |
+----------+ +-------+-------+-------+-------+
^ ^ ^ ^
| | | |
&p[0] == p + 0 -------+ | | |
&p[1] == p + 1 ---------------+ | |
&p[2] == p + 2 -----------------------+ |
&p[3] == p + 3 -------------------------------+
4. Was passiert nun wenn Du in Deinem Fall folgende Situation hast:
struct file {
/* irgendwas drin */
};
typedef struct file *File;
int count = 42;
File *files = (File *)calloc(count, sizeof(File *));
Um das wirklich verstehen zu können ist es hier sinnvoll, die typedefs wegzulassen:
struct file **files = (struct file **)calloc(count, sizeof(struct file **));
Zuerst der calloc-Aufruf: Es wird hier ein Speicherbereich allozierst, der 42 Elemente enthält, die so groß sind wie der Typ "struct file **", also ein Zeiger auf einen Zeiger auf struct file. Was Du aber eigentlich allozieren willst ist ein Array von Zeigern auf struct file (struct file ** - hier meint das zweite * das Array während das erste * dann die Zeiger auf die einzelnen Blöcke selbst meint - Pointerdeklarationen in C liest man von Rechts nach Links). Das heißt: Du willst eigentlich folgendes Speicherbild haben:
+------ Typ (schematisch, keine korrekte Syntax):
| ((struct file *)*) files;
| \___________/ |
| | |
| Zeier auf struct file Array von
|
| jedes dieser Elemente ist
| von der Größe sizeof(struct file *)
| _____ _____ _____ _____
| / \ / \ / \ / \
v +----------+ +-------+-------+-------+-------+- ...
files: | 0x..... -|----------->| 0x... | 0x... | 0x... | 0x... | ...
+----------+ +-------+-------+-------+-------+- ...
^ ^ ^ ^
| | | |
&files[0] == files + 0 -------+ | | |
&files[1] == files + 1 ---------------+ | |
&files[2] == files + 2 -----------------------+ |
&files[3] == files + 3 -------------------------------+
Da jedes Element eigentlich vom Typ "struct file *" sein soll müsstest Du eigentlich sizeof(struct file *) angeben und *nicht* wie Du es gemacht hast sizeof(struct file **). Da alle Zeiger auf Daten in C die gleiche Größe haben müssen, ist sizeof(struct file **) genau so groß wie sizeof(struct file *) - hier an der Stelle bist Du durch Zufall aus der Klemme.
Wenn Du dann jedoch folgende Zeile hast:
files[i] = (struct file *)malloc(sizeof(struct file *));
Dann hast Du ein Problem. Denn hier willst Du eigentlich folgende Speichersituation haben:
+------------- Hat den Typ (schematisch, keine valide Syntax):
| (struct file *)
| \___________/
| Zeiger auf struct file
|
| Hat die Größe sizeof(struct file)!
| ________________________________
| / \
| +--------+ +----------------------------------+
files[i]: | 0x... -|---->| Hier die eigentliche struct file |
+--------+ +----------------------------------+
Dann schießt Du Dir hiermit selbst ins Knie. Denn Deine struct file ist um ein vielfaches Größer als der Speicherbereich, der für einen Zeiger zur Verfügung steht - und damit überschreibst Du Speicher, der gar nicht mehr zum eigentlich allozierten Bereich gehört, was Dir in andere Teile des Programms reinspielt, was dann an einer VÖLLIG ANDEREN Stelle zu einem Absturz führt, weil die Daten dort nicht mehr konsistent sind.
Erst einmal: So geht es richtig:
files[i] = (struct file *)malloc(sizeof(struct file));
Oder, wenn Du unbedingt bei Deinem typedef auf einen Zeiger bleiben willst (ich halte das wie gesagt für sehr gefährlich, weil es Informationen vor einem versteckt!), dann:
files[i] = (File)malloc(sizeof(*File));
sizeof(*File) wäre hier die Größe des Typs auf den File ein Zeiger ist.
########################### MERKREGEL #############################
# #
# Es gibt IMMER, IMMER, IMMER, IMMER, IMMER eine Differenz von #
# GENAU EINEM Stern (*) zwischen dem Typen, den man von malloc, #
# calloc, realloc oder alloca zurückbekommen will und dem Typen, #
# den man in da sizeof()-Argument der Funktion packt! #
# #
###################################################################
Als weiterer, genereller Tipp: Der Fehler, das man in C über einen Speicherbereich hinausschreibt, ist relativ häufig. Daher gibt es Tools, mit denen man so etwas erkennen kann. Eines davon nennt sich valgrind. Das erkennt, solche Speicherzugriffe und merkt sich, an welcher Stelle im Programm sie passieren (man muss das Programm natürlich mit Debug-Symbolen kompilieren, damit das funktioniert).
Valgrind kann außerdem noch viel mehr:
* Zugriff auf uninitialisierten Speicher erkennen, bspw.:
int a; printf ("%d\n", a);
Allerdings nicht nur auf dem Stack, was moderne Compiler inzwischen
selbst erkennen, sondern auch auf dem Heap, was ein Compiler nicht
erkennen kann.
* Memory-Leaks erkennen, d.h. wenn Du Speicher, den Du per malloc, calloc
oder realloc alloziert hast mit free() nicht mehr freigibst.
* Einige andere Dinge, die ebenfalls mit Speicherverwaltung zu tun haben.
Beachte aber, dass die Systembibliotheken einige uninitialisierte Speicherzugriffe beim Start der Anwendung vornehmen, d.h. wenn Du in libc.so oder ld.so ganz am Anfang des Programms uninitialisierte Speicherzugriffe gemeldet bekommst, dann ist das nicht zwangsweise ein Fehler in Deinem Programm. Am besten Du probierst valgrind mal mit einem billigen "Hello World"-Programm aus, das garantiert keine derartigen Fehler enthält und genau die Meldungen, die da auch kommen, kannst Du grundsätzlich ignorieren. Alle anderen aber nicht!
Viele Grüße,
Christian
Hallo,
danke nochmals für deine ausführliche Erklärung. Ab der Mitte musste ich es mir nochmals durchlesen, aber ich glaube, dass mir allmählich der Knopf aufgeht. :)
Ich habe letztendlich auch deinen Rat befolgt und typedef entfernt. Eines dabei ist aber verwunderlich.
files[i] = (File)malloc(sizeof(*File));
So etwas schrieb ich schon mal, weil ich offensichtlich schon mal auf der richtigen Fährte war. Es lässt sich aber nicht kompilieren, zumindest nicht mit gcc, als welchem Grund auch immer. Ich dachte, dass ich wieder mal Blödsinn dachte und fing wieder an zu basteln.
Jedenfalls ist nun auch das Verhalten erklärt, warum die Abstürze stets "am Schluss" passierten, da einfach der Platz nicht mehr vorhanden war. Den Fehler hätte ich wohl nie gefunden, da ich dort, wo er eig. verursacht wird, gar nicht gesucht habe.
Valgrind scheint sehr vielversprechend zu sein. Werd's mir gleich mal ansehen.
Viele Grüße,
Markus
Hallo Markus,
files[i] = (File)malloc(sizeof(*File));
>
> So etwas schrieb ich schon mal, weil ich offensichtlich schon mal auf der richtigen Fährte war. Es lässt sich aber nicht kompilieren, zumindest nicht mit gcc, als welchem Grund auch immer.
Hmm, da habe ich mich vertan, das geht nicht für Typen, sondern nur für Variablen. Im Prinzip kannst folgendes machen:
`files[i] = (File)malloc(sizeof(*(files[i])));`{:.language-c}
Wie gesagt würde ich aber von typedefs auf Pointer \*SEHR STARK\* abraten. Das obige kommt als weiterer Grund, warum man das vermeiden möchte, dazu.
Viele Grüße,
Christian
--
[Mein "Weblog"](http://del.icio.us/chris_se/servertipps) [[RSS](http://del.icio.us/rss/chris_se/servertipps)]
[Using XSLT to create JSON output](http://www.christian-seiler.de/projekte/xslt-json/) (Saxon-B 9.0 for Java)
»I don't believe you can call yourself a web developer until you've built an app that uses hyperlinks for deletion and have all your data deleted by a search bot.«
-- [Kommentar bei TDWTF](http://thedailywtf.com/Comments/WellIntentioned-Destruction.aspx#254549)
Hallo Markus,
Oh, den Satz hatte ich ganz übersehen, daher Nachtrag:
Ich habe letztendlich auch deinen Rat befolgt und typedef entfernt.
Ganz entfernen musst Du ihn ja nicht - es reicht aus, wenn Du den Zeiger aus dem typedef entfernst, dann musst Du nicht immer mehr nur struct schreiben. Also:
struct file {
/* ... */
};
typedef struct file File;
Oder kürzer:
typedef struct file {
/* ... */
} File;
Und dann überall statt "struct file" halt "File" schreiben, d.h.
File **files = (File **)calloc(count, sizeof(File *));
files[0] = (File *)malloc(sizeof(File));
etc.
Viele Grüße,
Christian
Hallo Markus,
Valgrind scheint sehr vielversprechend zu sein. Werd's mir gleich mal ansehen.
Was ich noch erwähnen wollte: Seitdem ich Valgrind kenne lasse ich es grundsätzlich über *alle* meine C-Programme, die ich schreibe, laufen - auch wenn sie zu funktionieren scheinen. Das deckt nämlich verborgene Fehler auf, die vielleicht in dem Moment keine Rolle spielen, später aber zu ernsten, schwer zu debuggenden Problemen werden. Von daher kann ich jedem C-Programmierer nur empfehlen, Valgrind (oder etwas vergleichbares) auch schon immer während der Entwicklung zu benutzen - und nicht nur, wenn Fehler auftreten.
Viele Grüße,
Christian
Hallo,
für das User Manual von Valgrind muss ich mir wahrscheinlich schon mal 2 Tage zeit nehmen. Ich bin sicher, dass die Zeit aber gut investiert ist :)
danke nochmals und viele Grüße,
Markus
Hallo,
ich habe dazu doch noch eine Frage:
Wie kann ich mir das erklären....
#include <stdio.h>
int main(void) {
char* string = "Test";
char* p_test = string;
printf("%c\n", string[2]);
printf("%c\n", p_test[2]);
//Weder das...
string[2]='x';
//noch das funktioniert...
p_test[2]='x';
return 0;
}
...es führt beides zu einem Segmentation Fault. Wie kann ich mir das erklären? Ich dachte es wäre gleich mal ein guter Test für Valgrind:
Valgrind spuckt folgendes aus:
markus@archy ~/C-Programme $ valgrind --leak-check=full --track-origins=yes ./valgrindtest
==3453== Memcheck, a memory error detector
==3453== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==3453== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==3453== Command: ./valgrindtest
==3453==
s
s
==3453==
==3453== Process terminating with default action of signal 11 (SIGSEGV)
==3453== Bad permissions for mapped region at address 0x8048502
==3453== at 0x8048420: main (valgrindtest.c:12)
==3453==
==3453== HEAP SUMMARY:
==3453== in use at exit: 0 bytes in 0 blocks
==3453== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3453==
==3453== All heap blocks were freed -- no leaks are possible
==3453==
==3453== For counts of detected and suppressed errors, rerun with: -v
==3453== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 13 from 8)
Speicherzugriffsfehler
Seit wann ist es nicht erlaubt, Zeichen in einem Array zu überschreiben?
Viele Grüße,
Markus
Hi,
char* string = "Test";
char* p_test = string;printf("%c\n", string[2]);
printf("%c\n", p_test[2]);//Weder das...
string[2]='x';
//noch das funktioniert...
p_test[2]='x';
...es führt beides zu einem Segmentation Fault. Wie kann ich mir das erklären?
ich vermute, dass der konstante String "Test" als Bytefolge 54 65 73 74 00 im Codesegment abgelegt wird, das durch den daraus resultierenden Prozess nicht beschrieben werden darf.
Seit wann ist es nicht erlaubt, Zeichen in einem Array zu überschreiben?
Kommt drauf an, in welchen Segment sie liegen (was der zuständige Descriptor für Berechtigungen angibt).
Ciao,
Martin
Hallo Markus,
Wie kann ich mir das erklären....
char* string = "Test";
Eigentlich ist das strenggenommen falsch und hier liegt der Hund begraben. Denn "Test" ist ein String-Literal und hat eigentlich den Typ const char *, d.h. der Inhalt darf nicht modifiziert werden (const). Wenn du es an ein char * zuweist (und damit das const unter den Tisch kehrst, was Dir Dein Compiler mindestens mit einer Warnung quittieren sollte), dann hängt es von den Umständen ab, was genau passiert, wenn Du es doch versuchst zu modifizieren. In Deinem Fall halt Programmabsturz. Auf einigen Plattformen "funktioniert" es vielleicht auch, aber eben nicht ohne Nebeneffekte.
Seit wann ist es nicht erlaubt, Zeichen in einem Array zu überschreiben?
Weil Du _kein_ Array hast und auch kein Zeiger auf ein Array (wie Du es von malloc zurückbekommst) sondern einen Zeiger auf ein String-Literal (was laut Standard nicht modifiziert werden darf). Wenn Du ein Array deklarierst, dann funktioniert das auch:
char string[] = "Test";
char *p = string;
string[0] = 'f';
p[1] = 'a';
Das legt Dir einen Array auf dem Stack an und den kannst Du später modifizieren - oder wie hier eben auch einen Zeiger auf das Array (bzw. wie in C halt üblich auf das erste Element des Arrays) erhalten und das dann über den Zeiger modifizieren.
Aber auch hier nochmal sicherheitshalber die Warnung: Der Array hier ist auf dem Stack angelegt und ist nicht mehr gültig, wenn die Funktion beendet wird. Das heißt: Du kannst einen Zeiger auf dieses Array nicht zurückgeben genausowenig wie Du einen Zeiger auf irgend eine andere lokale Variable zurückgeben kannst.
Alternativ kannst Du natürlich immer einen Zeiger auf ein Array auf dem Heap anfordern, den kannst Du dann auch zurückgeben:
char *string = (char *)malloc(sizeof("Test"));
strcpy(string, "Test");
char *p = string;
string[0] = 'f';
p[1] = 'a';
Viele Grüße,
Christian
Hallo Christian,
danke für die Info. Wenn es als "const" erkannt wird, ist die Sache natürlich klar.
Viele Grüße,
Markus
Moin.
Eigentlich ist das strenggenommen falsch und hier liegt der Hund begraben. Denn "Test" ist ein String-Literal und hat eigentlich den Typ const char *, d.h. der Inhalt darf nicht modifiziert werden (const).
Prinzipiell korrekt, im Detail jedoch nicht ganz richtig: String-Literale haben Array-Typ ("hello"
ist beispielsweise vom Typ char [6]
- wären sie Zeiger, würde z.B. sizeof
für jedes Literal identische Werte liefern.
Dass ein String-Literal nicht modifiziert werden darf, liegt nicht an seinem Typ (laut Standard ist es aus vermutlich historischen Gründen nicht const
), sondern an folgendem Absatz:
It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.
Christoph