Question

General problem: implementing a dictionary.

Specific problem: according to gdb, my allocations and everything are working exactly as intended. The problem arises once types_dict_add is called with a key of "c". Everything is once again allocated as it should be and the entire dictionary is intact throughout the function context (all pointers are accessible and the structures at those pointers have their set values), but once the function exits back to main, the mydict pointers are all out of bounds. The actual pointer addresses stored in the mydict are all unchanged, but are suddenly inaccessible. Up until the call with "c", the mydict is also intact and as it should be in main.c

What am I doing wrong here? It's really confusing me as I've checked with gdb over and over again and the len value is being set correctly so there is definitely a proper amount of space being realloc'd each go.

types.h

#ifndef _TYPES_H_
#define _TYPES_H_

union types {
    long int ival;
    char *sval;
};

struct kv {
    char* key;
    union types val;
};

struct kv    **types_dict_init();
void           types_dict_add(struct kv**, char*, union types);
struct kv     *types_dict_get(char*);
void           types_dict_free(struct kv**);

#endif

types.c

struct kv **
types_dict_init()
{
    struct kv **newdict;

    newdict = calloc(1, sizeof(struct kv*));
    newdict[0] = NULL;

    return (newdict);
}

void
types_dict_add(struct kv** d, char* k, union types v)
{
    int len;

    if (d[0] == NULL) {
        d = realloc(d, sizeof(struct kv*) * 2);
        d[0] = malloc(sizeof(struct kv));
        d[0]->key = k;
        d[0]->val = v;
        d[1] = NULL;
    } else {
        for (len = 1; d[len - 1] != NULL; len++)
            ;
        d = realloc(d, sizeof(struct kv*) * ++len);
        d[len - 2] = malloc(sizeof(struct kv));
        d[len - 2]->key = k;
        d[len - 2]->val = v;
        d[len - 1] = NULL;
    }
}

main.c

#include <stdio.h>
#include <stdlib.h>

#include "bencode/types.h"

int main(int argc, char **argv)
{
    struct kv **mydict;
    union types toadd;

    toadd.sval = "test";
    mydict = types_dict_init();
    types_dict_add(mydict, "a", toadd);
    types_dict_add(mydict, "b", toadd);
    types_dict_add(mydict, "c", toadd);
    types_dict_add(mydict, "d", toadd);

    for(int i = 0; i < 4; i++)
        printf("%d: %s\n", i, mydict[i]->key);

    return (EXIT_SUCCESS);
}
Was it helpful?

Solution

In types_dict_add you modify the local variable d. This change is not reflected in main's variable mydict, because C passes function arguments by value. You will need to add an extra level of indirection if you want types_dict_add to be able to do realloc on mydict.

I think it would be simpler to use length-counting instead of a terminating NULL; then you can make your controlling structure be something like:

struct dict
{
    struct kv **members;
    size_t n_members;
};

This would also avoid a triple pointer (some people are uncomfortable with those).

OTHER TIPS

Always check the return value of realloc. realloc may fail in some cases.

d = realloc(d, sizeof(struct kv*) * 2);

do the check before accessing d.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top