Speicherlecks bei asynchronen Funktionsaufrufen?
-
Hallo.
In in C noch ziemlich neu, konnte aber bereits ein erstes Programm durch autonomes Lernen schreiben. Die Daemon-Applikation verhält sich nach ca. 1 Stunde seltsam und stürzt kurz darauf mit einem Speicherzugriffsfehler ab. Ich vermute, dass Speicherlecks im Programmcode sind, zumal ich mich mit malloc() und free() nicht gut auskenne, da ich nur andere Sprachen wie z.B. Delphi gewohnt bin, bei denen Ressourcenschutzblöcke recht einfach sind und wenig mit Pointern zu tun haben.
Ein Grundbestandteil meines Programmes sind asynchrone Funktionsaufrufe, die absolut detached sind (ja, tatsächlich gewollt), im Hintergrund laufen und sich überschneiden können (aber wahrscheinlich nie werden, weil sie sich stets selbst beenden). Ich vermute, dass die übergegebenen Parameter nicht freigegeben werden. Diese Aufrufe finden exakt jede Minute statt, weswegen ein Speicherleck hier den Speicher schnell erschöpfen kann.
Ich könnte den asynchronen Part auf folgenden Beispielcode reduzieren:
void async_function_call( void* (*start_routine)(void*), void* arg ) { pthread_t mythread; pthread_attr_t tattr; pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); pthread_create( &mythread, &tattr, start_routine, arg ); // mythread oder tattr freigeben? } struct koordinate { int x; int y; }; void* meine_funktion(void* data) { struct koordinate* k = (struct koordinate*)data; int x = (*k).x; int y = (*k).y; std::cout << "Hallo World!" << ::std::endl; std::cout << x << ::std::endl; std::cout << y << ::std::endl; // k oder data freigeben? return 0; } int main(void) { while (true) { struct koordinate k; // korrekt erstellt? k.x = 3; k.y = 5; async_function_call(meine_funktion, &k); // k kann hier nicht freigegeben werden, da die ASync-Funktion das für sich regeln muss sleep(1); } return 0; }
Seht ihr hier unsaubere Stellen, die bei Langzeitbenutzung einen Fehler verursachen könnten?
Ich habe bereits versucht, das in meine_funktion() erzeugte struct k mit delete zu entfernen, ging aber nicht. Ich habe alles mögliche schon durchprobiert: delete k, delete &k, free(k), free(&k), free(k*), free(*k) ... Das selbe bei der Threadvariable mythread.
Eine große Verständnisfrage habe ich zusätzlich: Ist es in C nicht so, dass lokale Variablen innerhalb einer Methode automatisch freigegeben werden? Oder ist es wirklich so, dass ich alles freigeben muss? Ich kann mir das irgendwie nicht richtig vorstellen, da bei anderen Programmiersprachen der komplette Aktivierungssatz einer Methode aus dem Stack entfernt wird, wenn die Methode fertig ist.
PS: Ich habe auch mtrace() versucht und mich an die Anleitung im Internet gehalten, doch statt der tollen Ausgabe, die genau sagt, in welcher Zeile ein Speicherleck entsteht sehe ich nur die hexadezimalen Speicheradressen...
Kann mir jemand konstruktive Vorschläge oder Hilfestellung bei der Bugsuche geben? Ich bitte darum, bei Vorschlägen etwas ausführlich zu sein, da ich mit den meisten C-Spezifischen Ausdrücken im Moment nicht viel anfangen kann.
Vielen Dank schonmal im Voraus.
Gruß
blackdrakePS: Wenn sich aus dem oben genannten Extrakt-Code nichts erschließen lässt, kann ich ebenfalls den kompletten Code (460 Zeilen mit Kommentaren) posten, in dem sich eventuell noch unsaubere Stellen befinden. Ich möchte das Thema aber nicht sprengen. Eigentlich macht das Programm nichts anderes, als jede Minute URLs aus einer Datenbank auszulesen und diese aufzurufen sowie die Verfügbarkeit zu notieren. Jedoch müssen die Abstände exakt 60 Sekunden sein und alle Webseiten zur selben Zeit aufgerufen werden (deswegen die ASync-Funktionsaufrufe)
-
blackdrake schrieb:
void async_function_call( void* (*start_routine)(void*), void* arg ) { pthread_t mythread; pthread_attr_t tattr; pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); pthread_create( &mythread, &tattr, start_routine, arg ); // mythread oder tattr freigeben? }
tattr solltest du Fall mittels mit pthread_attr_destroy freigeben.
Mit mythread brauchst du nichts mehr machen, da er ja schon als detachter Thread erzeugt wurde.
while (true) { struct koordinate k; // korrekt erstellt? k.x = 3; k.y = 5; async_function_call(meine_funktion, &k); // k kann hier nicht freigegeben werden, da die ASync-Funktion das für sich regeln muss sleep(1); }
Das ist problematisch. k ist als lokale Variable nur während der Ausführung des umschließenden Blocks, also während eines Schleifendurchgangs gültig. Du übergibt aber den Zeiger an den Thread, der eventuell auch später darauf zugreift.
Genauer gesagt wird k auf dem Stack gespeichert, und das nächste k im nächsten Schleifendurchgang wird wieder an der selben Stelle im Speicher gespeichert; d.h. wenn doch mehrere der Threads gleichzeig laufen, greifen sie auf dasselbe k zu.
Und wenn du die while-Schleife irgendwann verlassen würdest, könnte etwas ganz anderes an der Stelle auf dem Stack gespeichert werden, und die Threads würden immer noch darauf zugreifen.
Du solltest stattdessen k mit malloc reservieren, also
struct koordinate *k = malloc(sizeof *k);
, und am Ende von meine_funktion mir free(k) freigeben.
Ich habe bereits versucht, das in meine_funktion() erzeugte struct k mit delete zu entfernen, ging aber nicht. Ich habe alles mögliche schon durchprobiert: delete k, delete &k, free(k), free(&k), free(k*), free(*k) ... Das selbe bei der Threadvariable mythread.
Was man einfach so als lokale Variable hinschreibt, wird auf dem Stack gespeichert und muss nicht explizit freigegeben werden.
Alles, was man mit malloc (oder realloc) erzeugt, muss man irgendwo mit free wieder freigeben.
new und delete ist C++.
-
Hallo.
Vielen Dank für deine ausführliche Antwort!
- Das Attribut habe ich mittels pthread_attr_destroy() nach dem Aufruf von pthread_create() freigegeben.
static void async_function_call( void* (*start_routine)(void*), void* arg ) { pthread_t mythread; pthread_attr_t tattr; pthread_attr_init(&tattr); pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); pthread_create( &mythread, &tattr, start_routine, arg ); pthread_attr_destroy(&tattr); }
- Das struct habe ich nun mit malloc reserviert und gebe es innerhalb der asynchronen Funktion wieder frei.
Dein Code war leider nicht lauffähig. Ich habe etwas herumgespielt und konnte schließlich kompilieren, aber irgendwie habe ich trotzdem was mit diesen vielen Pointern verwurstet.
// struct koordinate *k = malloc(sizeof koordinate); // error: expected primary-expression before ')' token // struct koordinate *k = malloc(sizeof *k); // error: invalid conversion from 'void*' to 'koordinate*' // struct koordinate *k = malloc(sizeof(struct koordinate)); // error: invalid conversion from 'void*' to 'koordinate*' // struct koordinate *k = malloc(sizeof(struct koordinate*)); // error: invalid conversion from 'void*' to 'koordinate*' struct koordinate *k = (struct koordinate*) malloc(sizeof(struct koordinate)); // Das funktioniert ebenfalls beim Compilieren: struct koordinate *k; k = (struct koordinate*) malloc(sizeof(struct koordinate)); k->x = 3; k->y = 7; async_function_call(meine_funktion, k); // <-- Speicherzugriffsfehler (auch mal mit &k und (void*)k probiert)
Da ich auf dem Bildschirm nur die Meldung "Speicherzugriffsfehler" sehe, ohne jegliche Zusatzinformation, habe ich mittels "cout" die Stelle gesucht, bei der der Fehler auftritt. Die aufzurufende Funktion meine_funktion() wird nicht vor dem Programmabsturz betreten.
PS: Ich bin mir nicht ganz sicher, ob ich nun konkret das Ansi-C oder C++ benutze (ich hoffe, dass ich im korrekten Subforum bin). Ich kompiliere mit GCC und meine Datei heißt *.c. Ich vermute, dass es ein Ansi-C-Programm ist, weil meine Methoden nicht in einer Klasse liegen, sondern in loser Form. Sollte ich - da ich einen C++ Compiler habe, statt malloc das sicherere "new"-Konstrukt verwenden?
Gruß
blackdrake
-
blackdrake schrieb:
Dein Code war leider nicht lauffähig. Ich habe etwas herumgespielt und konnte schließlich kompilieren, aber irgendwie habe ich trotzdem was mit diesen vielen Pointern verwurstet.
[...]struct koordinate *k; k = (struct koordinate*) malloc(sizeof(struct koordinate));
Ja stimmt, bei C++ muss man das Ergebnis von malloc wohl casten.
async_function_call(meine_funktion, k); // <-- Speicherzugriffsfehler (auch mal mit &k und (void*)k probiert)
Da ich auf dem Bildschirm nur die Meldung "Speicherzugriffsfehler" sehe, ohne jegliche Zusatzinformation, habe ich mittels "cout" die Stelle gesucht, bei der der Fehler auftritt. Die aufzurufende Funktion meine_funktion() wird nicht vor dem Programmabsturz betreten.
Der Aufruf mit k ist schon richtig, der Fehler muss irgendwo anders sein.
Speicherzugriffsfehler sind leider schwierig zu finden, da sie oft an einer anderen Stelle auftreten als der eigentliche Fehler.
PS: Ich bin mir nicht ganz sicher, ob ich nun konkret das Ansi-C oder C++ benutze (ich hoffe, dass ich im korrekten Subforum bin). Ich kompiliere mit GCC und meine Datei heißt *.c. Ich vermute, dass es ein Ansi-C-Programm ist, weil meine Methoden nicht in einer Klasse liegen, sondern in loser Form.
Das Programm besteht größtenteils aus C (bis auf die Ausgabe mit cout, dazu benutzt man in C printf), aber du kompilierst es als C++, also wahrscheinlich mit g++ statt gcc.
Sollte ich - da ich einen C++ Compiler habe, statt malloc das sicherere "new"-Konstrukt verwenden?
Du hast bestimmt auch einen C-Compiler. Du solltest dich eben entscheiden, ob du C oder C++ benutzen willst. Bei C++ verwendet man besser new/delete statt malloc/free, und Klassen statt Strukturen, und Methoden statt loser Funktionen.
Man kann natürlich auch in C++ so programmieren wie in C, aber das ist dann sinnlos und sehr unschön.
-
Hallo.
Naja, dann bin ich aber irgendwie hin und hergerissen. Eigentlich gefallen mir die Grundsätze von C besser und ich bin in diesem kleinen Daemon noch nicht auf abstrakte Datentypen angewiesen. Andererseits gefällt mir das new und delete besser (und es ist scheinbar auch sicherer als free und *alloc)
Folgender Code hat jetzt funktioniert:
struct koordinate* k = new struct koordinate; k->x = 3; k->y = 7; async_function_call(meine_funktion, k);
In meine_funktion dann ein "delete k".
Löst das jetzt dieses Memory-Leak?
Dieses Programm treibt mich noch zum Wahnsinn... Bereits nach wenigen Minuten stürzt es mit der AV ab und ich weiß nicht mehr, wo ich suchen soll. Mit Memory-Leaks ist das aktuelle Verhalten irgendwie nicht mehr zu erklären...
PS: Weißt du, wieso "mtrace mein-programm trace.txt" mir nur binäre Speicheradressen hinwirft und nicht die Programmzeile ausdruckt?
Gruß
blackdrake
-
blackdrake schrieb:
Andererseits gefällt mir das new und delete besser (und es ist scheinbar auch sicherer als free und *alloc)
malloc ist aber eigentlich nicht unsicherer als new. Vielleicht etwas weniger komfortabel; in richtigen Programmen sollte man auch noch den Rückgabewert von malloc auf NULL überprüfen.
Von daher ist auch nicht zu erklären, wieso es mit new statt malloc jetzt plötzlich funktioniert, da stimmt wahrscheinlich noch irgendwas anderes nicht.
Zu mtrace weiß ich auch nichts. Aber Memory Leaks sollten ohnehin keine Programmabstürze verursachen.
-
Nun, ich werde mich mal dahinterklemmen und werde mich für C++ oder C entscheiden. Im Moment ist es ja eher ein Mix mit einer starken Tendenz zu C, was mich aber bisher nicht sonderlich stört.
Mit dem malloc() muss ich mir das nochmal anschauen. Vielleicht habe ich den Code falsch hingeschrieben? (Werde ich zu einem späteren Zeitpunkt nocheinmal probieren, um auf C-Standard zu kommen)
Jedenfalls ist im Moment das Problem mit dem Speicher das größte. Ich glaube mittlerweile den wahren Problemauslöser (aber nicht den Grund) gefunden zu haben: http://www.c-plusplus.net/forum/viewtopic-var-p-is-1656236.html#1656236 ... Oh man, den ganzen Abend 500 Zeilen Code auf 38 Zeilen abstrahiert und ich weiß immer noch nicht, was los ist :-s Seltsam, seltsam...
Folgende Fragen bleiben für den Thread noch offen:
- Wie geht man exakt mit malloc() bezüglich den struct's um? (Das was ich gemacht hab war ja wie "Bingo" spielen, solange rumspielen bis der Compiler es akzeptiert hat - ich weiß nicht, ob mein Code korrekt war, da der curl-bug im Moment alles überdeckt)
- Ist es sinnvoll, komplett auf C abzusteigen und einen C-Compiler zu nutzen (gehen die libraries curl, mysql und pthread dann noch?) - wie muss ich beim Umsteig vorgehen?
- Habe ich alle Memoryleaks in meinem Programm gefunden?Ich werde es in einem späteren Test mal versuchen, Byte für Byte zu ermitteln.
-
blackdrake schrieb:
Folgende Fragen bleiben für den Thread noch offen:
- Wie geht man exakt mit malloc() bezüglich den struct's um? (Das was ich gemacht hab war ja wie "Bingo" spielen, solange rumspielen bis der Compiler es akzeptiert hatDa ist eigentlich nichts kompliziertes dabei. Man übergibt malloc eine bestimmte Größe und bekommt einen Pointer auf einen Speicherbereich, auf den man dann speichern kann was man will.
Ein Struct ist nichts weiter als ein paar Variablen hintereinander (eventuell mit Lücken dazwischen zwecks Alignment), das hat eine bestimmte Größe, die man entweder mit sizeof(struct mystruct) herausfinden kann oder indem man es als Typ für eine Variable verwendet und deren Größe mit sizeof bestimmt.
Also entweder
struct mystruct *x = malloc(sizeof(struct mystruct));
oder
struct mystruct *x = malloc(sizeof *x);
Die letzte Möglichkeit hat den Vorteil, dass man den Typ nicht zweimal hinschreiben muss (man darf aber den Stern hinter sizeof nicht vergessen, sonst wird nur die Größe eines Pointers verwendet).
Bei C++ muss man den void-Pointer von malloc noch explizit casten, bei C ist das unnötig und etwas albern.
- Ist es sinnvoll, komplett auf C abzusteigen und einen C-Compiler zu nutzen (gehen die libraries curl, mysql und pthread dann noch?) - wie muss ich beim Umsteig vorgehen?
"Absteigen" würde ich es nicht nennen, das klingt ja so als wäre C++ was besseres
Gerade von den Open-Source-Libraries sind die meisten ohnehin C-Libraries; wenn man viele davon benutzt ist es also naheliegend, auch selber C zu verwenden.
- Habe ich alle Memoryleaks in meinem Programm gefunden?
Ich werde es in einem späteren Test mal versuchen, Byte für Byte zu ermitteln.
Du musst einfach nur sicherstellen, dass du zu jedem malloc ein passendes free aufrufst, und zu jeder Initialisierungsfunktion aus irgendeiner Library die entsprechende Freigabefunktion.
-
Hallo.
namespace invader schrieb:
Also entweder
struct mystruct *x = malloc(sizeof(struct mystruct));
oder
struct mystruct *x = malloc(sizeof *x);
Die letzte Möglichkeit hat den Vorteil, dass man den Typ nicht zweimal hinschreiben muss (man darf aber den Stern hinter sizeof nicht vergessen, sonst wird nur die Größe eines Pointers verwendet).
Bei C++ muss man den void-Pointer von malloc noch explizit casten, bei C ist das unnötig und etwas albern.
Die zweite Variante kommt mir irgendwie komisch vor. Wie kann ich sizeof *x kennen, obwohl ich gerade dabei bin, *x zu definieren? Find ich irgendwie etwas verwirrend und der C++ Compiler mochte das auch nicht. Ist dieses malloc() dann der Konstruktor der Struct oder muss vorher noch etwas definiert/initialisiert werden?
Ich muss mir unbedingt mal einen Linux-C Kompiler suchen...
namespace invader schrieb:
Du musst einfach nur sicherstellen, dass du zu jedem malloc ein passendes free aufrufst, und zu jeder Initialisierungsfunktion aus irgendeiner Library die entsprechende Freigabefunktion.
Nun, ich glaube, es gibt noch mehr Ursachen für Speicherlecks. In Delphi habe ich es mal hinbekommen, die Auslagerungsdatei zu füllen, sodass die Windows-Session abstürzte. Ich habe dann herausgefunden, dass eine String-Funktion, dessen Ergebnis direkt in ein Funktionsargument fließt, nicht freigegeben wird. (siehe weiter unten)
Ich habe zu folgendem Code ein paar Fragen bzw. Bedenken:
static int StrToInt(string text) { // Leakfrei? Ersetzbar? return atoi(text.c_str()); } static string IntToStr(int Value) { // Leakfrei? Ersetzbar? ostringstream ostr; ostr << Value; return ostr.str(); } static void* pwb_neuer_durchlauf(void*) { MYSQL_RES* result = NULL; // MySQL-Result MYSQL_ROW row; // MySQL-Zeile int m = 5; // dyn. result = _mysql_query("SELECT `id`, `url` FROM `...` WHERE `status` = '" + IntToStr(m) + "'"); // Leak? if (result != NULL) { if (mysql_num_rows(result) > 0) { row = mysql_fetch_row(result); while (row) { struct mystruct *k = new struct mystruct; k->id = StrToInt(row[0]); k->url = row[1]; // Deskriptorenübertragung? async_function_call(meine_funktion, k); row = mysql_fetch_row(result); } } } return 0; }
Zeile 1 und 5:
sind die beiden Funktionen StrToInt() und IntToStr() frei von Speicherleaks?
Zeile 17:
Was passiert mit diesem zusammengebastelten String und dem Ergebnis von IntToStr(m)? Werden diese am Ende wieder korrekt freigegeben, oder bleibt der ganze dynamisch erzeugte String im Speicher (so wie es bei Delphi passiert ist)? Da es sich bei diesem dynamisch erzeugten String nicht um eine lokale Variable handelt, bezweifle ich, dass C weiß, dass man ihn freigeben muss.
Zeile 25:
Was passiert hier mit std::string? Werden hier die Deskriptoren übertragen oder der komplette String? Würde nur eine Referenzierung des Strings von row[1] auf k übertragen werden, hätte ich ein riesen Problem bei den Threads, da row sich ja bei jeder MySQL-Zeile ändert. Muss man den String also vorher klonen oder wie?
Zeile 17 und 24:
Kann ich IntToStr() und StrToInt() irgendwie abschaffen? Was bietet mir C für meine Bedüfnisse?
Von sprintf() bin ich ja nicht so begeistert, da es nur C-Strings mit bestimmter Länge annimmt. Und wenn die MySQL-Query aufgrund der %d und %s Eingaben zu lang wird, passiert bestimmt irgendwas seltsames. Ich wollte da lieber dynamischer sein.
Gruß
blackdrake
-
blackdrake schrieb:
Die zweite Variante kommt mir irgendwie komisch vor. Wie kann ich sizeof *x kennen, obwohl ich gerade dabei bin, *x zu definieren?
Der Typ und damit die Größe ist ja an der Stelle schon bekannt.
Ist dieses malloc() dann der Konstruktor der Struct oder muss vorher noch etwas definiert/initialisiert werden?
Konstruktoren gibt es nur bei C++ (deswegen muss man dort für Klassen new verwenden, das ruft den Konstruktor auf). Der von malloc gelieferte Speicherbereich enthält einfach das, was vorher zufällig an der Stelle im Speicher stand. Wenn man es initialisiert haben möchte, muss man es eben selber initialisieren.
Ich muss mir unbedingt mal einen Linux-C Kompiler suchen...
Nimm doch einfach gcc...
sind die beiden Funktionen StrToInt() und IntToStr() frei von Speicherleaks?
Ich denke schon, aber das ist C++. string gibt es in C nicht.
Zeile 17:
Was passiert mit diesem zusammengebastelten String und dem Ergebnis von IntToStr(m)? Werden diese am Ende wieder korrekt freigegeben, oder bleibt der ganze dynamisch erzeugte String im Speicher
Der string-Destruktor dürfte automatisch aufgerufen werden und alles wieder freigeben.
Da es sich bei diesem dynamisch erzeugten String nicht um eine lokale Variable handelt, bezweifle ich, dass C weiß, dass man ihn freigeben muss.
In C geht das natürlich nicht. Da würde man das eher so machen:
#define SIZE 256; // groß genug char buffer[SIZE]; snprintf(buffer, SIZE, "SELECT blabla status='%d'", m);
buffer wäre dann nur auf dem Stack und muss nicht extra freigegeben werden.
Was passiert hier mit std::string? Werden hier die Deskriptoren übertragen oder der komplette String? Würde nur eine Referenzierung des Strings von row[1] auf k übertragen werden, hätte ich ein riesen Problem bei den Threads, da row sich ja bei jeder MySQL-Zeile ändert. Muss man den String also vorher klonen oder wie?
Was ist denn k->url überhaupt? Wenn es nur ein Zeiger ist, musst du das, auf das er zeigt, natürlich auch kopieren. Wenn es ein Objekt einer C++-Klasse ist, erledigt das vielleicht schon der zugehörige Zuweisungsoperator.
Von sprintf() bin ich ja nicht so begeistert, da es nur C-Strings mit bestimmter Länge annimmt. Und wenn die MySQL-Query aufgrund der %d und %s Eingaben zu lang wird, passiert bestimmt irgendwas seltsames. Ich wollte da lieber dynamischer sein.
Dann muss man eben vorher ermitteln, wieviel Platz man braucht, und dann mit malloc reservieren.
-
Hallo.
namespace invader schrieb:
Nimm doch einfach gcc...
OK, werde ich machen. Ich verwende ja im Moment noch g++ (ich dachte das wäre das gleiche). Ich lass mich mal überraschen, wie viel ich migrieren muss, bis von meinem Mixcode ich auf echten C-Standard komme.
namespace invader schrieb:
Ich denke schon, aber das ist C++. string gibt es in C nicht.
Och nö, dann muss ich ja doch einiges abändern.
Ich hatte mich schon gefreut, dass man mit string so mehr oder weniger toll arbeiten kann... Naja, Luxus ist das Arbeiten mit Strings in C/C++ im Vergleich zu anderen Sprachen trotzdem nicht. Ich schau mal, wo ich überall Std-String nach C-String ohne Probleme wandeln kann.
namespace invader schrieb:
Was ist denn k->url überhaupt?
k->url ist ein std::string in meinem struct.
Ich weiß aber leider nicht, wie C/C++ das mit den Strings genau behandelt. In Java sind Strings abstrakte Datentypen, also Klassen und werden immer nur referenziert, nicht kopiert. Und ich befürchte, dass das in C++ auch so ist. Da mir in C/C++ aber die speichertechnischen/tiefgreifenden Grundlagen fehlen, weiß ich nicht genau, was da im Speicher passiert und wo ich vorsichtig sein muss.
Es gäbe also 2 Fälle, bei denen ich Klarheit bräuchte:
1. Fall C++: k->url ist ein std::string. Wäre dann k->url = row[1] eine String-Kopie oder nur eine Deskriptorenübertragung (Pointer-Kopie)?
2. Fall C: k->url ist ein c-string/pchar. Dann wäre doch k->url = row[1] eine Übertragung der Deskriptoren und damit äußerst problematisch wegen den Threads, oder?Wenn nur die Deskriptoren, also der Pointer übertragen wird, muss ich den Std-String/C-String erst klonen, bevor ich ihn in k->url reinkopieren kann, sodass der Thread exakt diesen String erhält und keinen Pointer, der beim nächsten MySQL-Row verworfen wird.
Blöd ist jetzt nur, dass ich gar nicht so genau weiß, von welchem Typ row[1] ist... Ich habe schon nach MYSQL_ROW gegoogelt aber nichts genaueres daraus lesen können. Ich vermute, dass der Typ MYSQL_ROW eine Art Array ist, der C-Strings/PChar's beinhaltet. Bin mir aber nicht sicher...
// Edit: Bin nun endlich auf vollwertiges Ansi-C umgestiegen und habe C++ Aufwärtskompatibilitäts mit Compilerflags behalten
Gruß
blackdrake