dynamische String Array

Florian Strienz

Erfahrenes Mitglied
Hallo zusammen,
ich komme eigentlich aus der Java Ecke, muss aber aktuell C Code von 19XX auf XML umprogrammieren. Funktioniert auch ganz gut alles. Ich habe nur ein kleines Problem.

Ich benötige ein Array in dem ich beliebig viele Strings abspeichern kann. Meine Idee war, ein Pointer Array auf char Pointer.

static char** cnptr=NULL;

Wenn ich weiss, wieviele Spalten ich brauche, reserviere ich den Speicher

cnptr = (char **) calloc(doc_anz_spalten, sizeof(**cnptr));

Und weise die Werte aus dem XML in einer for Schleife zu
Code:
for(int i = 0; i < doc_anz_spalten; i++) {
		xmlItem = xmlNodeGetContent(node->nodeTab[i]);
		char *string = new char[strlen((char *)xmlItem)+1];
		strcpy( string, (char *)xmlItem);
		cnptr[i]=string;
		xmlFree(xmlItem);
}

Das funktioniert auch perfekt. Aber leider kriege ich beim Speicher freigeben eine Speicherschutz verletztung im Heap.
Code:
if(cnptr !=NULL){
		for(int i=0;i<doc_anz_spalten;i++){
			delete cnptr[i];
		}
		free(cnptr);
		cnptr = NULL;
}

Kann mir jemand hier weiterhelfen? Was mache ich falsch. Wo ist der Denkfehler. Bin ja schon überrascht, dass der erste Teil funktioniert. ;)
Gruß
Flo
 
static char** cnptr=NULL;

Wenn ich weiss, wieviele Spalten ich brauche, reserviere ich den Speicher

cnptr = (char **) calloc(doc_anz_spalten, sizeof(**cnptr));
sizeof(**cnptr) entspricht hier sizeof(char). Du willst allerdings sizeof(char *).

Und weise die Werte aus dem XML in einer for Schleife zu
Code:
for(int i = 0; i < doc_anz_spalten; i++) {
		xmlItem = xmlNodeGetContent(node->nodeTab[i]);
		char *string = new char[strlen((char *)xmlItem)+1];
new gibt es in C nicht, nur in C++. Es wäre am besten, wenn du einheitlich new/delete oder malloc/free verwendest.

Der Rest scheint i.O. zu sein.

Grüße,
Matthias
 
Danke für die Antwort.

Ich kompiliere mit vs 2008. Da müsste es doch wurscht sein, wenn ich das mische, oder? Der Compiler schluckt es.

Wie würde das mit malloc aussehen? Das habe ich probiert, da kriege ich aber schon bei dem ausgeben der Variablen Speicherverletzungen. ;)
 
Ich benötige ein Array in dem ich beliebig viele Strings abspeichern kann.

Wenn du nicht auf C festgelegt bist, kannst du doch sowas machen (C++):
C++:
#include <string>
#include <vector>

...

std::vector<std::string> vec_strings;

for( int i = 0; i < doc_anz_spalten; i++)
{
    xmlItem = xmlNodeGetContent(node->nodeTab[i]);
    vec_strings.push_back(xmlItem);
    xmlFree(xmlItem);
}
Damit sparst du dir das händische Speichermanagment.

Gruß
MCoder
 
Ich kompiliere mit vs 2008. Da müsste es doch wurscht sein, wenn ich das mische, oder? Der Compiler schluckt es.
Ja, funktionieren sollte es. Ich möchte dann aber nicht derjenige sein, der diesen Code in Zukunft warten muss. Verwendet man nämlich einmal free statt delete (oder andersrum), so ergibt das keinen Compilerfehler, aber zur Laufzeit kann beliebig viel schief gehen. Wenn man konsequent nur free verwendet (und dementsprechend auch malloc & Co.), muss man sich deswegen keine Gedanken machen.

Wie würde das mit malloc aussehen? Das habe ich probiert, da kriege ich aber schon bei dem ausgeben der Variablen Speicherverletzungen. ;)
In etwa so:
C:
char *string = malloc(strlen((char *)xmlItem) + 1);

Grüße,
Matthias
 
Guten morgen,
ich habe deine Anpassungen ausprobiert. Jetzt bekomme ich nicht mehr bei i==6 die Speicherverletzung sondern immer bei i==9, also beim letzten Element.

Critical error detected c0000374
Windows has triggered a breakpoint in xmlTest.exe.

This may be due to a corruption of the heap, which indicates a bug in xmlTest.exe or any of the DLLs it has loaded.

This may also be due to the user pressing F12 while xmlTest.exe has focus.

The output window may have more diagnostic information.
First-chance exception at 0x7799ef37 in xmlTest.exe: 0xC0000374: Ein Heap wurde beschädigt.
The program '[872] xmlTest.exe: Native' has exited with code -1073740940 (0xc0000374).

Code:
...
		cnptr = (char **) calloc(doc_anz_spalten, sizeof(*cnptr));

		doc_anz_spalten=node->nodeNr;

		for( i = 0; i < node->nodeNr; i++ ) {
			xmlItem = xmlGetProp(node->nodeTab[i], (xmlChar *)"desc");
			
			printf ("%s : ", (char *)xmlItem);
			xmlFree(xmlItem);

			xmlItem = xmlNodeGetContent(node->nodeTab[i]);
			printf ("%d : ", strlen((char *)xmlItem)+1);
			char *string = (char *) malloc(strlen((char *)xmlItem) + 1);
			strcpy( string, (char *)xmlItem);
		
			cnptr[i]=string;
			printf ("%s \n", (char *)xmlItem);
			xmlFree(xmlItem);
		}

		for(i = 0; i < node->nodeNr; i++){
			printf ("%s\n", cnptr[i]);
		}
		
		if(cnptr !=NULL){
			for(int i=0;i<doc_anz_spalten;i++){
				free(cnptr[i]);
			}
			free(cnptr);
			cnptr = NULL;
		}
...
 
Mit dem Vector funktioniert es. Das ist ja fast wie Java. ;) Die clear Methode räumt den ganzen Speicher auf, oder? Mehr muss ich nicht machen?

Mich würde aber trotzdem interessieren, was an meinen C Code falsch ist.
 
Hi.
Mit dem Vector funktioniert es. Das ist ja fast wie Java. ;) Die clear Methode räumt den ganzen Speicher auf, oder?
Die clear Methode leert den vector, d.h. dessen Größe ist dann 0. Der Speicher wird erst freigegeben wenn der vector zerstört und somit der Destruktor aufgerufen wird.
Mehr muss ich nicht machen?
Man muss gar nichts machen. Die STL Klassen funktionieren nach dem RAII Prinzip :google:
Mich würde aber trotzdem interessieren, was an meinen C Code falsch ist.
In welcher Zeile tritt der Fehler auf? Schalte mal das Exception Handling vom Debugger an (Menü Debug->Ausnahmen -> C++ Ausnahmen).

Gruß
 
ich habe deine Anpassungen ausprobiert. Jetzt bekomme ich nicht mehr bei i==6 die Speicherverletzung sondern immer bei i==9, also beim letzten Element.
In welcher der drei Schleifen kracht es denn? Für mich sieht alles in Ordnung aus. Folgender Code macht bei mir auch keine Probleme:
C:
#include <malloc.h>
#include <stdio.h>
#include <string.h>

const char* xmlItems[] = {
  "Neque", "porro", "quisquam", "est", "qui", "dolorem", "ipsum", "quia",
  "dolor", "sit", "amet", "consectetur", "adipisci", "velit"
};

int main() {
  int doc_anz_spalten = sizeof(xmlItems) / sizeof(xmlItems[0]);
  char **cnptr = calloc(doc_anz_spalten, sizeof(char*));
  int i;

  for (i = 0; i < doc_anz_spalten; i++) {
    const char *xmlItem = xmlItems[i];
    char *string = malloc(strlen(xmlItem)+1);
    strcpy(string, xmlItem);
    cnptr[i] = string;
  }

  for (i = 0; i < doc_anz_spalten; i++) {
    printf("%s\n", cnptr[i]);
  }

  if (cnptr != NULL) {
    for (i = 0; i < doc_anz_spalten; i++) {
      free(cnptr[i]);
    }
    free(cnptr);
    cnptr = NULL;
  }

  return 0;
}

Mit dem Vector funktioniert es. Das ist ja fast wie Java. ;) Die clear Methode räumt den ganzen Speicher auf, oder? Mehr muss ich nicht machen?
clear räumt die string-Instanzen auf, ja. Das würde aber auch der Destruktor machen. Du musst also kein clear aufrufen, wenn du im Anschluss den Vektor sowieso löschst.

Grüße,
Matthias
 
Danke für die Tipps. Die habe mich zur Lösung gebracht

Ich habe in meinem test code zuviel rumprobiert und nach dem Anpassungen von Matthias hat sich noch ein Zugriff auf den Inhalt versteckt, nach dem ich in frei geben habe... :eek:
Der ursprüngliche Fehler war also wirklich der Falsche calloc.

Ich hätte es genauer schreiben sollen. Wenn ich einen Vector clear() e, dann wird der Speichern, den der Inhalt belegt hat automatisch freigeben, oder? Nach dem docu Studium bin ich mir jetzt aber eigentlich sicher, dass es so ist. (Der vector wird immer wieder verwendet, da es mehrere durchläufe gibt)

VIELEN DANK! Ihr habt mir sehr weitergeholfen.

Eine generelle Frage hätte ich noch. Ist es nicht Sinnvoll, wenn ich den Code jetzt Stückweise auf C++ umstelle (also den Vector verwenden), als das ganze weiter in C zu pflegen?
 

Neue Beiträge

Zurück