1

I would like to know how to malloc (and hen realloc) an array of a structure. Here is my structure :

typedef struct tag {
    char *key;
    char *val;
} tag;

I use this structure in another structure :

typedef struct node {
    int id;
    double lat;
    double lon;
    int visible; 
    tag *tab;
} node;

I define a node *n, then:

n->tab = (tag*)malloc(sizeof(tag) * 5);

but I have an error of malloc corruption.

void remplisTag(xmlNodePtr cur, node *n) {
    xmlNodePtr fils;
    n->tab = malloc(sizeof(*n->tab) * 5);
    if (n->tab == NULL) {
        error(E_ERROR_MALLOC);
    }
    printf("apres malloc\n");
    int taille = 5;
    int ind = 0;
    xmlAttrPtr attr1, attr2;
    xmlChar *key;
    xmlChar *value;
    fils = cur->xmlChildrenNode;
    fils = fils->next;
    while (xmlStrcmp(fils->name, (const xmlChar*)"text") != 0) {
        if (xmlStrcmp(fils->name, (const xmlChar*)"tag") == 0) {
            if (ind == taille - 1) {
                n->tab = realloc(n->tab, sizeof(tag) * (taille + 5));
                taille = taille + 5;
            } else {
                taille = taille;
            }
            /* searching for key */
            attr1 = xmlHasProp(fils, (const xmlChar*)"k");
            if (attr1 == NULL) {
                error(E_KEY);
            } else {
                key = xmlGetProp(fils, (const xmlChar*)"k");
                if (key == NULL) {
                    error(E_KEY_V);
                }
                /* searching for value */
                attr2 = xmlHasProp(fils, (const xmlChar*)"v");
                if (attr2 == NULL) {
                    error(E_VALUE); 
                }
                value = xmlGetProp(fils, (const xmlChar*)"v");
                if (value == NULL) {
                    error(E_VALUE_V);
                }
                tag t;
                t.key = malloc(sizeof((char*)key));
                strcpy(t.key, (char*)key);
                strcpy(t.val, (char*)value);
                t.val = malloc(sizeof((char*)value));
                n->tab[ind++] = t;
            }
        }
        fils = fils->next;
    }
    free(n->tab);
}

In main:

node *n = malloc(sizeof(node));
xmlNodePtr cur;

in a while loop:

remplisTag(cur, n);
chqrlie
  • 131,814
  • 10
  • 121
  • 189
venilla
  • 61
  • 1
  • 9

2 Answers2

2

There is a memory allocation issue in the following lines

tag t;
t.key = malloc(sizeof((char*)key));
strcpy(t.key, (char*)key);
strcpy(t.val, (char*)value);
t.val = malloc(sizeof((char*)value));

The memory in C is very fiddly - when you allocate memory, you need to hold enough to store the data.

tag t;
t.key = malloc(sizeof((char*)key));
strcpy(t.key, (char*)key);

Creates enough for a pointer -which is probably not enough.

The fix is something like.

tag t;
t.key = strdup( key );
t.val = strdup( value );

The strdup function combines the malloc (of the correct size) and the strcpy.

mksteve
  • 12,614
  • 3
  • 28
  • 50
1

There is no visible problem in your allocation statement, some people prefer that malloc() return value not be cast to the destination type, and it is slightly more reliable to use the type of the destination pointer to avoid type mismatches that would be hard to detect:

n->tab = malloc(sizeof(*n->tab) * 5);
  • What is the precise error message you get?
  • Is it a runtime error?
  • How did you allocate the node structure that n points to?
  • Can you post the full code to the function that produces this failure?

A runtime message from malloc() is an indication that the memory allocation structures used internally by malloc have been corrupted. It would indicate that the problem is elsewhere, probably a buffer overrun in another object allocated by malloc(). Check the code that was executed since the previous call to malloc().

EDIT:

In the code posted, there are some allocation errors:

            t.key = malloc(sizeof((char*)key));
            strcpy(t.key, (char*)key);
            strcpy(t.val, (char*)value);
            t.val = malloc(sizeof((char*)value));
  • The space you allocate is just the size of a pointer char*, not the length of the string plus 1 for the final null terminator. If key is longer than 3 or 7 bytes, depending on the architecture, you have a buffer overrun.
  • t.val is allocated after you copy contents to it. Undefined behavior!

You should simplify this with strdup():

            t.key = strdup((char*)key);
            t.val = strdup((char*)value);

Your test for reallocation is too conservative: the array should be reallocated when ind == taille:

        if (ind == taille) {
            n->tab = realloc(n->tab, sizeof(*n->tab) * (taille + 5));
            taille += 5;
        }

taille and ind should be stored into the node to keep track of how much space has been allocated and how many tags are present. The current code does not provide this information to the caller, the rest of the tag array is uninitialized, there is no way to tell, undefined behavior is lurking.

Note that there are too many casts in your code. Casts prevent some type mismatch detection by the compiler, it is wise to change the argument types for your functions to avoid unnecessary casts.

For example: libxml2 defines xmlChar as a typedef for unsigned char. This is a very bad design decision. They should use char and handle the strings correctly regardless of whether char happens to be signed or unsigned on the current environment. This choice forces programmers to cast most arguments to the xmlXXX APIs, making the code ugly and error prone. You cannot change that, but you could use inline functions to convert between char* and xmlChar* to keep casts to a minimum.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thanks for your answer. I think the problem comes when i try to realloc this array: ` n->tab=realloc(n->tab,sizeof(tag)*(taille+5));` i have this error: **Error in `./parser': malloc(): memory corruption: 0x09c7c538** Actually i'm not able to find out which part of my code gives me a faillure error, because i use 2 functions.. – venilla Feb 28 '16 at 15:54
  • @kuttyV Then show that code from the start. Your current code has no reallocs so nobody can guess what's going on. – Sami Kuhmonen Feb 28 '16 at 15:56
  • @kuttyV: `realloc` is a prolific source of bugs: the reallocated object may and usually does have a different address. If you have pending pointers that point into the previous object location, writing data through these pointers invokes undefined behavior, possibly corrupting the malloc internal state. Post more code. – chqrlie Feb 28 '16 at 15:59
  • It's funny: On the one hand you say, casts should be removed to "prevent some type mismatch detection", where on the other hand you consider casting malloc (where it exactly _enables_ type mismatch detection) bad, too. – Ctx Feb 28 '16 at 16:31
  • @Ctx: my stance on casting `malloc()` return value is this: if you can use the destination type, **do not** cast the return value, if you cannot and need to use `sizeof(type)`, then **do** cast the return value to `(type*)` so a type mismatch can be detected. Destination type cannot be used when the allocated pointer os returned from a function, passed as a function or macro argument, or used in an initializer. – chqrlie Feb 28 '16 at 16:37
  • @Ctx: casts are indeed a plague, but automatic `void*` conversion hides type mismatches too. Balancing these two evils is advisable. – chqrlie Feb 28 '16 at 16:39
  • @chqrlie Yes, I fully agree. I just thought the two advices seem a bit contradictory, because your second argument _supports_ casting malloc (correctly). – Ctx Feb 28 '16 at 16:44
  • 1
    @Ctx: I usually hide this cast with a macro: `#define malloc_array(type, n) ((type*)malloc(sizeof(type) * (n)))`. The classic argument against casting because it would hide the omission of `` is moot if one compiles with the appropriate warnings enabled. – chqrlie Feb 28 '16 at 16:48
  • @chqrlie Yes, this is indeed a good way! – Ctx Feb 28 '16 at 16:51
  • @chqrlie : Thank you :) – venilla Feb 28 '16 at 18:02