[Gelöst] C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

theSplit

1998
Registriert
3 Aug. 2014
Beiträge
5.857
Hallo,

ich habe gerade etwas Code mit Valkyrie (/Valgrind Frontend) und den "gcc" Compilger Flags "-Ox -g" überprüft und kann gerade nicht nachvollziehen, warum eine bestimmte Code Zeile Memory Leaked.

Folgender Fehler:
4 bytes in 1 blocks are definitely lost in loss record 1 of X

Ich habe eine Struktur mit einem Char-Pointer >char *name<, diese hier, ein Auszug:

[src=c]typedef struct xmlNode {
void *lastAddedElement;
char *name; // <--- um den Namen geht es indirekt!
struct keyValuePair *keyValues;
} xmlNode;[/src]


Jetzt habe ich eine Funktion die einen XML Knoten Zeichenweise in ein statisches Char Array >readData< einliest welches jedoch jeweils nur einen eingelesen Wert enthält den Namen, den Schlüssel bzw. den Wert des Schlüssels.

ReadData wurde, für die ganzen Funktion global, so definiert:
[src=c]char readData[1024];[/src]


Nachdem der Wert eingelesen ist setzte ich an dem "Endpunkt" des Wortes das Null Zeichen und verarbeite das "readData" wie folgt:

Und dabei hebe ich mal die Stelle vor, die ich so nicht verstehe:

[src=c]if (writerPos != 0) {
readData[writerPos] = '\0'; // Hier wird schon explizit gesetzt....

if (!xmlHasName) {
xmlTag->name = malloc(sizeof(char) * (writerPos + 1)); // Diese Zeile wirft das Memory Leak laut Valgrind/Valkyrie
strcpy(xmlTag->name, readData); // bzw. hier.

xmlHasName = true;
} else if (isKey) {
//....
xmlKeyValue->key = malloc(sizeof(char) * (writerPos + 1)); // Wirft kein Leak, auch ohne Umweg
strcpy(xmlKeyValue->key, readData);
//....

isKey = false;
isValue = true;
} else if (isValue) {
xmlKeyValue->value = malloc(sizeof(char) * (writerPos + 1)); // Wirft auch kein Leak, auch ohne den Umweg
strcpy(xmlKeyValue->value, readData);

isValue = false;
}

readData[0] = '\0'; // <-- Ohne diese Stelle bekomme ich ein Memory Leak bei xmlTag->name = malloc....
writerPos = 0;
}[/src]


Und jetzt die Frage/Problem dazu, es wirft ausschließlich der >char* name< mit Malloc und Null-Platzhalter ein Leak, obwohl ich das gleiche mache wie auch für die anderen Werte, Schlüssel/Wert.
Bei letzterem bekomme ich keine Meldung über Speicheraustritt, nur der Name des XML Tags macht komischerweise Probleme* und das ist der erste Wert der gesetzt wird.

Warum bekomme ich diesen nicht, wenn ich explizit >readData[0] = '\0'< setze?
 
Zuletzt bearbeitet:
Re: C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

Schwierig zu sagen ohne mehr Kontext. Wo/wie stellst du sicher, dass die Pointer, die du mallocst, nicht noch auf allokierten Speicher zeigen? Also wie sind die frees?
 
  • Thread Starter Thread Starter
  • #3
Re: C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

Sobald ich >readData[0]< auf >'\0'< setze, verschwindet der Fehler.

Ich habe die Zeile auch schon auskommentiert, bzw. das > strcpy(xmlTag->name, readData) < und dann leaked es nicht mehr. Alles andere funktioniert.
Wenn ich den Namen nicht in den Speicher kopieren lasse wird auch nichts geleaked.

Ich verstehe dann aber nicht wieso ich bei einem statischen Char-Array zusätzlich dieses "zurücksetzen"/"nullen" muß.

Eine Aufräum Routine sieht so aus:

[src=c]
void freeXMLCollection(xmlDataCollection *xmlCollection) {

xmlNode *xmlTag = NULL;

for (unsigned int i = 0; i < xmlCollection->count; ++i) {
xmlTag = &xmlCollection->nodes;

for (unsigned int j = 0; j < xmlTag->keyValuePairs; ++j) {
free(xmlTag->keyValues[j].key);
free(xmlTag->keyValues[j].value);
}

for (unsigned int j = 0; j < xmlTag->wordCount; ++j) {
free(xmlTag->words[j].data);
}

for (unsigned int j = 0; j < xmlTag->wTagCount; ++j) {
freeXMLCollectionTag(&xmlTag->wikiTags[j]);
}

free(xmlTag->name); // Wird hier freigegeben!
free(xmlTag->keyValues);
free(xmlTag->words);
free(xmlTag->entities);
free(xmlTag->wikiTags);
}

free(xmlCollection->nodes);
free(xmlCollection->openNodes);

if (debug) {
printf("[DEBUG] Successfully cleaned up xmlCollection...\n");
}

return;
}[/src]
 
Zuletzt bearbeitet:
  • Thread Starter Thread Starter
  • #4
Re: C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

Ich stehe gerade auf dem Schlauch, wie so immer bei diesen Problemen... nun ja.

Erstmal was genau mir Valkyrie/Valgrind so sagen will, wenn ich mit Memcheck checke:

Bildschirmfoto von »2016-07-22 21-03-06«.png


und

Bildschirmfoto von »2016-07-22 21-04-00«.png



Sorry falls das jetzt schon wieder zig Zeilen Code sind, aber ich glaube ohne wird nicht ersichtlich was falsch läuft...
und ich kann den Fehler einfach so nicht nachvollziehen... - wäre schön wenn jemand mir einen Tip geben könnte wo ich suchen muß.

Vielleicht verstehe ich auch nicht ganz die Ausgabe in Valkyrie von Valgrind/Memcheck...

Die Routine, und nur diese scheint ja Probleme zu machen, sieht so aus:

[src=c]int parseXMLNode(const int xmlTagStart, const unsigned int lineLength, const unsigned int currentLine, const char *line, xmlDataCollection *xmlCollection) {
xmlCollection->nodes = (xmlNode*) realloc(xmlCollection->nodes, sizeof(xmlNode) * (xmlCollection->count + 1));
xmlNode *xmlTag = &xmlCollection->nodes[xmlCollection->count];

/*
typedef struct xmlNode {
bool isClosed;
bool isDataNode;
unsigned short indent;
short lastAddedType; // 0 WORD, 1 WIKITAG, 2 ENTITY
unsigned int keyValuePairs;
unsigned int wordCount;
unsigned int entityCount;
unsigned int wTagCount;
unsigned int start;
unsigned int end;
unsigned int lineCount;
void *lastAddedElement;
char *name;
struct keyValuePair *keyValues;
struct word *words;
struct entity *entities;
struct wikiTag *wikiTags;
} xmlNode;
*/

xmlTag->isClosed = false;
xmlTag->isDataNode = false;
xmlTag->indent = xmlTagStart;
xmlTag->lastAddedType = -1; // 0 WORD, 1 WIKITAG, 2 ENTITY
xmlTag->keyValuePairs = 0;
xmlTag->wordCount = 0;
xmlTag->entityCount = 0;
xmlTag->wTagCount = 0;
xmlTag->start = currentLine;
xmlTag->end = 0;
xmlTag->lineCount = 0;
xmlTag->lastAddedElement = NULL;
xmlTag->name = NULL;
xmlTag->keyValues = NULL;
xmlTag->words = NULL;
xmlTag->entities = NULL;
xmlTag->wikiTags = NULL;


// Routine variables
unsigned int readerPos = xmlTagStart+1;

char readIn = '\0';
char readData[xmlNodeReaderBuffer] = "\0";
int writerPos = 0;

bool xmlHasName = false;
bool isKey = false;
bool isValue = false;

bool nodeClosed = false;
keyValuePair *xmlKeyValue = NULL;


// Identify the xmlTag name and key and value pairs
while (line[readerPos] != '>') {
while (line[readerPos] != '>') {
readIn = line[readerPos];

if (!isValue) {
if (readIn == ' ') {
++readerPos;
break;
} else if (readIn == '=') {
readerPos += 2;
isKey = true;
break;
} else if (readIn == '/') {
xmlTag->isClosed = true;
xmlTag->end = currentLine;
readerPos++;
continue;
}
} else if (readIn == '"') {
++readerPos;
break;
}

readData[writerPos] = readIn;
++writerPos;
++readerPos;
continue;
}

if (writerPos != 0) {
readData[writerPos] = '\0';

if (!xmlHasName) {
xmlTag->name = malloc(sizeof(char) * (writerPos + 1));
strcpy(xmlTag->name, readData);

xmlHasName = true;
} else if (isKey) {
xmlTag->keyValues = (keyValuePair*) realloc(xmlTag->keyValues, sizeof(keyValuePair) * (xmlTag->keyValuePairs + 1) );

xmlKeyValue = &xmlTag->keyValues[xmlTag->keyValuePairs];
xmlKeyValue->value = NULL;

xmlKeyValue->key = malloc(sizeof(char) * (writerPos + 1));
strcpy(xmlKeyValue->key, readData);

++xmlTag->keyValuePairs;

isKey = false;
isValue = true;
} else if (isValue) {
xmlKeyValue->value = malloc(sizeof(char) * (writerPos + 1));
strcpy(xmlKeyValue->value, readData);

isValue = false;
}

writerPos = 0;
readData[0] = '\0';
}

}

//--------------------------------------------------------------------------
// NOTE: Does adjust so we know if data follows the xmlTag
++readerPos;

char *endPointer = strrchr(line, '<');
unsigned int xmlEnd = endPointer != NULL ? endPointer - line : 0;

if (readerPos > xmlEnd) {
xmlEnd = lineLength-1;

if (debug && beVerbose) {
printf("\n\n[DEBUG] RP: %8d | END: %8d | LINELENGTH: %d | NAME: %s\n", readerPos, xmlEnd, lineLength, xmlTag->name);
}
}

//--------------------------------------------------------------------------

if (xmlHasName) {
if (!xmlTag->isClosed) {
xmlCollection->openNodes = (unsigned int*) realloc(xmlCollection->openNodes, sizeof(unsigned int) * (xmlCollection->openNodeCount + 1));
xmlCollection->openNodes[xmlCollection->openNodeCount] = xmlCollection->count;
++xmlCollection->openNodeCount;
} else {
nodeClosed = false;
printf("%s\n", readData);
for (int i = xmlCollection->openNodeCount - 1; i != -1; --i) {
xmlNode *openXMLNode = &xmlCollection->nodes[xmlCollection->openNodes];
if (strcmp(xmlTag->name, openXMLNode->name) == 0) {
openXMLNode->isClosed = true;
openXMLNode->end = currentLine;

if (i < (xmlCollection->openNodeCount-1)) {
memmove(&xmlCollection->openNodes, &xmlCollection->openNodes[i+1], (xmlCollection->openNodeCount-(i+1)) * sizeof(unsigned int));

xmlCollection->openNodes = (unsigned int*) realloc(xmlCollection->openNodes, sizeof(unsigned int) * xmlCollection->openNodeCount);
--xmlCollection->openNodeCount;
}

if (beVerbose || debug) {
printf("[STATUS] | LINE: %8d | %sNODE CLOSED: '%s'\n", currentLine, openXMLNode->isDataNode ? "DATA " : "", openXMLNode->name);
}

nodeClosed = true;

break;
}
}

if (nodeClosed) {
return 0;
}

}

if (beVerbose || debug) {
printf("\n\n[STATUS] | LINE: %8d | %s%sNODE ADDED: '%s'\n", currentLine, xmlTag->isClosed ? "CLOSED " : "", readerPos < xmlEnd ? "DATA " : "", xmlTag->name);
for (unsigned int i = 0; i < xmlTag->keyValuePairs; ++i) {
printf("[STATUS] | KEYVALUE: '%s' > '%s'\n", xmlTag->keyValues.key, xmlTag->keyValues.value);
}
}

++xmlCollection->count;

} else {
return 0;
}

// Start process the data of the XML tag

if (readerPos < xmlEnd) {
xmlTag->isDataNode = true;
parseXMLData(readerPos, xmlEnd, currentLine, line, xmlTag);
}

return 1;
}[/src]


Die zwei die Aufräumen habe ich nochmal angepasst, sollten aber theoretissch allen Speicher freigeben:

[src=c]void freeXMLCollection(xmlDataCollection *xmlCollection) {

xmlNode *xmlTag = NULL;

for (unsigned int i = 0; i < xmlCollection->count; ++i) {
xmlTag = &xmlCollection->nodes;

for (unsigned int j = 0; j < xmlTag->keyValuePairs; ++j) {
free(xmlTag->keyValues[j].key);
free(xmlTag->keyValues[j].value);
}

for (unsigned int j = 0; j < xmlTag->wordCount; ++j) {
free(xmlTag->words[j].data);
}

for (unsigned int j = 0; j < xmlTag->wTagCount; ++j) {
freeXMLCollectionTag(&xmlTag->wikiTags[j]);
}

free(xmlTag->name);
free(xmlTag->keyValues);
free(xmlTag->words);
free(xmlTag->entities);
free(xmlTag->wikiTags);
}

free(xmlCollection->nodes);
free(xmlCollection->openNodes);

if (debug) {
printf("[DEBUG] Successfully cleaned up xmlCollection...\n");
}

return;
}

void freeXMLCollectionTag(wikiTag *wTag) {
/*
typedef struct wikiTag {
bool hasParentFormat;
short formatType;
unsigned short tagType;
unsigned short formatGroup;
unsigned short preSpacesCount;
unsigned short spacesCount;
unsigned int position;
unsigned int wordCount;
unsigned int wTagCount;
unsigned int entityCount;
char *target;
struct word *pipedWords;
struct wikiTag *pipedTags;
struct entity *pipedEntities;
} wikiTag;
*/

for (unsigned int i = 0; i < wTag->wordCount; ++i) {
free(wTag->pipedWords.data);
}

for (unsigned int i = 0; i < wTag->wTagCount; ++i) {
freeXMLCollectionTag(&wTag->pipedTags);
}

free(wTag->target);
free(wTag->pipedWords);
free(wTag->pipedTags);
free(wTag->pipedEntities);

return;
}[/src]
 
Re: C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

Ich seh das Speicherproblem auch nicht. Mag an der mangelnden Erfahrung mit Valgrind liegen. Was mir aufgefallen ist:

In parseXMLNode() hast du eine doppelte while-Schleife:
Code:
Expand Collapse Copy
      while (line[readerPos] != '>') {
        while (line[readerPos] != '>') {
Die äußere kannst du dir sparen bzw. falls nötig in ein if umwandeln. Sie wird nie öfter als 1x durchlaufen, weil readerPos nach der inneren Schleife nicht mehr verändert wird.

In freeXMLCollection():
Code:
Expand Collapse Copy
      free(xmlCollection->nodes);
      free(xmlCollection->openNodes);
Alles in openNodes ist tatsächlich non-owning und wurde schon bei der Schleife über die Nodes weggeräumt?
 
  • Thread Starter Thread Starter
  • #6
Re: C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

Danke für den Hinweis mit der doppelten While Schleife, die war in der Tat unnötig. :) Ich bin mir auch gerade nicht ganz sicher warum ich die dort drin hatte. ;)

*Anmerkung/Nachtrag - die zweite While Schleife war wohl doch nötig... damit alle Schlüssel korrekt eingelesen werden. ;)


OpenNodes ist vom Typ >unsigned int*< (also ein "unsigned int"-Array) und beinhaltet Knoten die geöffnet, aber nicht sofort in der selben Zeile gleich wieder geschlossen werden.

Ist ein Teil der xmlDataCollection, die sich wie folgt definiert:
[src=c]typedef struct xmlDataCollection {
unsigned int count;
unsigned int openNodeCount;
struct xmlNode *nodes;
unsigned int *openNodes;
} xmlDataCollection;[/src]

Dazu gehört auch die >xmlCollection< die daraus definiert wird.

Denke das klärt deinen Hinweis auf. :)

Aber danke für das Feedback. :T
 
Zuletzt bearbeitet:
  • Thread Starter Thread Starter
  • #7
Re: C - Frage zu Memory Leak mit Char-Pointer bzw. statischem Char-Array

@Brother John, du hast mir gerade DEN richtigen Hinweis gegeben.... vielleicht ohne es zu wissen. :D

Ich habe doch in dem ersten Code-Part, die Zeilen 173 bis 175, folgendes:
[src=c] if (nodeClosed) {
return 0;
}[/src]

Und danach erst, also nach diesem möglichen Return aus der Funktion, in Zeile 186, das hier:
[src=c] ++xmlCollection->count;[/src]

Das Problem dabei war also, ich habe, obwohl eine xmlNode hinzugefügt wurde, nie den Counter für die Anzahl erhöht wenn diese geschlossen war...

Jetzt bekomme ich keine Memory Leaks mehr!




Bzw. sieht der Funktion jetzt so aus:

[src=c] int parseXMLNode(const int xmlTagStart, const unsigned int lineLength, const unsigned int currentLine, const char *line, xmlDataCollection *xmlCollection) {
xmlCollection->nodes = (xmlNode*) realloc(xmlCollection->nodes, sizeof(xmlNode) * (xmlCollection->count + 1));
xmlNode *xmlTag = &xmlCollection->nodes[xmlCollection->count];

/*
typedef struct xmlNode {
bool isClosed;
bool isDataNode;
unsigned short indent;
short lastAddedType; // 0 WORD, 1 WIKITAG, 2 ENTITY
unsigned int keyValuePairs;
unsigned int wordCount;
unsigned int entityCount;
unsigned int wTagCount;
unsigned int start;
unsigned int end;
unsigned int lineCount;
void *lastAddedElement;
char *name;
struct keyValuePair *keyValues;
struct word *words;
struct entity *entities;
struct wikiTag *wikiTags;
} xmlNode;
*/

xmlTag->isClosed = false;
xmlTag->isDataNode = false;
xmlTag->indent = xmlTagStart;
xmlTag->lastAddedType = -1; // 0 WORD, 1 WIKITAG, 2 ENTITY
xmlTag->keyValuePairs = 0;
xmlTag->wordCount = 0;
xmlTag->entityCount = 0;
xmlTag->wTagCount = 0;
xmlTag->start = currentLine;
xmlTag->end = 0;
xmlTag->lineCount = 0;
xmlTag->lastAddedElement = NULL;
xmlTag->name = NULL;
xmlTag->keyValues = NULL;
xmlTag->words = NULL;
xmlTag->entities = NULL;
xmlTag->wikiTags = NULL;


// Routine variables
unsigned int readerPos = xmlTagStart+1;

char readIn = '\0';
char readData[xmlNodeReaderBuffer];
int writerPos = 0;

bool xmlHasName = false;
bool isKey = false;
bool isValue = false;

bool nodeClosed = false;
keyValuePair *xmlKeyValue = NULL;


// Identify the xmlTag name and key and value pairs
while (line[readerPos] != '>') {
readIn = line[readerPos];

if (!isValue) {
if (readIn == ' ') {
++readerPos;
break;
} else if (readIn == '=') {
readerPos += 2;
isKey = true;
break;
} else if (readIn == '/') {
xmlTag->isClosed = true;
xmlTag->end = currentLine;
readerPos++;
continue;
}
} else if (readIn == '"') {
++readerPos;
break;
}

readData[writerPos] = readIn;
++writerPos;
++readerPos;
continue;
}

if (writerPos != 0) {

readData[writerPos] = '\0';
if (!xmlHasName) {
xmlTag->name = malloc(sizeof(char) * (writerPos + 1));
strcpy(xmlTag->name, readData);

xmlHasName = true;
} else if (isKey) {
xmlTag->keyValues = (keyValuePair*) realloc(xmlTag->keyValues, sizeof(keyValuePair) * (xmlTag->keyValuePairs + 1) );

xmlKeyValue = &xmlTag->keyValues[xmlTag->keyValuePairs];
xmlKeyValue->value = NULL;

xmlKeyValue->key = malloc(sizeof(char) * (writerPos + 1));
strcpy(xmlKeyValue->key, readData);

++xmlTag->keyValuePairs;

isKey = false;
isValue = true;
} else if (isValue) {
xmlKeyValue->value = malloc(sizeof(char) * (writerPos + 1));
strcpy(xmlKeyValue->value, readData);

isValue = false;
}

writerPos = 0;
}

//--------------------------------------------------------------------------
// NOTE: Does adjust so we know if data follows the xmlTag
++readerPos;

char *endPointer = strrchr(line, '<');
unsigned int xmlEnd = endPointer != NULL ? endPointer - line : 0;

if (readerPos > xmlEnd) {
xmlEnd = lineLength-1;

if (debug && beVerbose) {
printf("\n\n[DEBUG] RP: %8d | END: %8d | LINELENGTH: %d | NAME: %s\n", readerPos, xmlEnd, lineLength, xmlTag->name);
}
}

//--------------------------------------------------------------------------

if (xmlHasName) {
++xmlCollection->count; // Das haette von anbeginn hier schon erhoeht werden muessen, so ists richtig!

if (!xmlTag->isClosed) {
xmlCollection->openNodes = (unsigned int*) realloc(xmlCollection->openNodes, sizeof(unsigned int) * (xmlCollection->openNodeCount + 1));
xmlCollection->openNodes[xmlCollection->openNodeCount] = xmlCollection->count - 1;
++xmlCollection->openNodeCount;
} else {
nodeClosed = false;
//printf("%s\n", readData);
for (int i = xmlCollection->openNodeCount - 1; i != -1; --i) {
xmlNode *openXMLNode = &xmlCollection->nodes[xmlCollection->openNodes];
if (strcmp(xmlTag->name, openXMLNode->name) == 0) {
openXMLNode->isClosed = true;
openXMLNode->end = currentLine;

if (i < (xmlCollection->openNodeCount-1)) {
memmove(&xmlCollection->openNodes, &xmlCollection->openNodes[i+1], (xmlCollection->openNodeCount-(i+1)) * sizeof(unsigned int));

xmlCollection->openNodes = (unsigned int*) realloc(xmlCollection->openNodes, sizeof(unsigned int) * xmlCollection->openNodeCount);
--xmlCollection->openNodeCount;
}

if (beVerbose || debug) {
printf("[STATUS] | LINE: %8d | %sNODE CLOSED: '%s'\n", currentLine, openXMLNode->isDataNode ? "DATA " : "", openXMLNode->name);
}

nodeClosed = true;

break;
}
}

if (nodeClosed)
return 1;
}

if (beVerbose || debug) {
printf("\n\n[STATUS] | LINE: %8d | %s%sNODE ADDED: '%s'\n", currentLine, xmlTag->isClosed ? "CLOSED " : "", readerPos < xmlEnd ? "DATA " : "", xmlTag->name);
for (unsigned int i = 0; i < xmlTag->keyValuePairs; ++i) {
printf("[STATUS] | KEYVALUE: '%s' > '%s'\n", xmlTag->keyValues.key, xmlTag->keyValues.value);
}
}
} else {
return 0;
}

// Start process the data of the XML tag

if (readerPos < xmlEnd) {
xmlTag->isDataNode = true;
parseXMLData(readerPos, xmlEnd, currentLine, line, xmlTag);
}

return 1;
}[/src]
 
Zuletzt bearbeitet:
Zurück
Oben