0

I'm working on some c code that dynamically allocates memory to an array of strings with a struct.

It basically can be initilized by DSA_INIT(name_of_variable) and strings can be appended with name_of_variable = arrcpy_str(name_of_variable, "some text"); and will append to the string, here is a small example:

DSA_INIT(test)
test = arrcpy_str(test, "hello world!");
// test.size is the amount of strings in the array, if test.size > 0 then it has values stored in it.
printf("Last value: %s\nLength: %d\n", test.contents[test.size - 1], test.size);
test = arrcpy_str(test, "test!");
printf("Last value: %s\nLength: %d\n", test.contents[test.size - 1], test.size);

Output:

Last value: hello world!
Length: 1
Last value: test!
Length: 2

As expected, but sometimes, quite bizarrely it breaks... I was writing another function for the windows operating system, it's quite complicated so instead of posting like 200 lines of code I'll just summarize what it does (in the parts where there are no issues):

os_listdir takes 2 parameters, the directory, and the index. it returns a char * to the index in alphabetical order starting at one of the directory.

e.g. C:\Users\usrname\Desktop contains 2 files: named a.txt and b.txt.

os_listdir("C:\Users\usrname\Desktop", 1) = ".",

os_listdir("C:\Users\usrname\Desktop", 2) = "..",

os_listdir("C:\Users\usrname\Desktop", 3) = "a.txt" and finally,

os_listdir("C:\Users\usrname\Desktop", 4) = "b.txt"

os_listdir returns "N/A" if the directory cannot be found or there are not enough files to get the index.

length is the same as strlen from string.h and strequals is the same as strcmp from string.h

os_isdir returns true if it is a directory and concat concatenates 2 strings. rstrip and replace work like pythons rstrip and replace (pretty self-explanatory for the rest ig)

My code doesnt seem to work however in this, for no seemingly reason.

array source:

struct DYNAMIC_STRING_ARRAY {
    char ** contents;
    int size;
};

#define DSA struct DYNAMIC_STRING_ARRAY

#define DSA_INIT(DSA_to_initilize) DSA DSA_to_initilize; DSA_to_initilize.size = 0;

/* Returns length of a string excluding null terminator */
int length(const char * __s) {
    int i;
    for (i = 0; __s[i] != '\0'; ++i);
    return i;
}
DSA arrcpy_str(DSA __a, char * to_add) {
    DSA __arr = __a;
    // It stops right below this line - it works normally but for some reason it stops in this function only:
    // Even if i try to do something like directory_list = arrcpy_str(directory_list, "test"); it still wont work.
    __arr.contents[__arr.size] = (char *) malloc(length(to_add) * sizeof(char));
    __arr.contents[__arr.size] = to_add;
    __arr.size++;
    return __arr;
}

DSA arrcpy(DSA __a, DSA __add) {
    DSA __arr = __a;
    for (int i = 0; i < __add.size; i++) {
        __arr.contents[__arr.size] = (char *) malloc(length(__add.contents[i]) * sizeof(char));
        __arr.contents[__arr.size + i] = __add.contents[i];
        __arr.size++;
    }
    return __arr;
}

and finally the os_listalldir source that is giving me trouble:

DSA os_listalldir(char * _path) {
    char * path = rstrip(replace(_path, '/', '\\'), '\\');
    DSA_INIT(directory_list)
    directory_list = arrcpy_str(directory_list, "test");
    if (!os_isdir(path)) {
        free(path);
        return directory_list;
    }

    int i = 1;
    char * c_directory = os_listdir(path, i);

    while (!strequals(c_directory, "N/A")) {
        i++;
        if (!strequals(c_directory, "..") && !strequals(c_directory, ".")) {
            char * full_path = concat(path, concat("\\", c_directory));
            // This line below is what breaks the code - refer to the source above to see where it actually stops
            directory_list = arrcpy_str(directory_list, full_path);
            if (os_isdir(full_path)) {
                directory_list = arrcpy(directory_list, os_listalldir(full_path));
            }
            printf("%s\n", full_path);
        }
        c_directory = os_listdir(path, i);
    }
    free(path);
    return directory_list;
}

The main problem being it stops running completely after the malloc part in arrcpy_str for some reason, I was wondering if someone could help me figure out why (it sometimes works but it sometimes doesn't??) however in the os_listalldir function it always stops running at that one commented line.

xihtyM
  • 240
  • 1
  • 2
  • 9
  • 3
    Where is `__arr.contents` allocated? – Eugene Sh. Jul 25 '22 at 21:08
  • ah sorry on the 4th line of my last src code i forgot to remove a test, it basically was to see if it stopped the entire thing, yeah it did (just a heads up). – xihtyM Jul 25 '22 at 21:10
  • @EugeneSh. __arr.contents is defined in the structure DYNAMIC_STRING_ARRAY and i only allocate to the strings inside of it - char ** contents; is in the struct and then I allocate with the function arrcpy_str, I am pretty sure it is fine to do that (I'm kinda new-ish to c so I might be wrong). – xihtyM Jul 25 '22 at 21:12
  • `DSA_INIT(directory_list)` creates a `DYNAMIC_STRING_ARRAY` with an uninitialized `contents` member. Then `arrcpy_str` tries to set `__arr.contents[__arr.size]` – 001 Jul 25 '22 at 21:14
  • How should I go about initilizing the contents member? – xihtyM Jul 25 '22 at 21:15
  • oh yeah i use loads of underscores cos they look cool lmao (i suck at naming variables) sos... – xihtyM Jul 25 '22 at 21:20
  • You can allocate it just like any pointer: `__arr.contents = malloc(sizeof(char *));` That allocates space for 1 pointer. To grow it, you can use `realloc`. – 001 Jul 25 '22 at 21:23
  • Would this work: `__arr.contents = (char **) realloc(__arr.contents, __arr.size * sizeof(char *));`? – xihtyM Jul 25 '22 at 21:29
  • Fix the two things wrong/improper with that statement, then maybe. 1. don't cast memory allocation function results in C, and 2. don't assume memory allocation functions succeed. – WhozCraig Jul 25 '22 at 21:30
  • @WhozCraig is it that bad to dynamically allocate stuff in c (also ur point two do u mean check if it points to null for a failed allocation) – xihtyM Jul 25 '22 at 21:34
  • Also, you need to update your macro so that it initializes the pointer to NULL. If you call `realloc` on an uninitialized pointer bad things can happen. – 001 Jul 25 '22 at 21:34
  • 1
    @xihtyM I never said it was "bad", I said those two mistakes should be fixed. Casting memory allocation results is unnecessary in C, and can subtly, and *easily*, hide broken code in the process. See [Do I cast malloc?](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). Regarding failure to check the results of your allocations (or IO operations, or anything else for that matter), assumptive programming in C is death. In the case of a failed `realloc` as you showed, you not only fail, you blast over the original memory pointer, which is now leaked into the ether. – WhozCraig Jul 25 '22 at 21:39
  • Thanks, 1 more thing: if it is a failed allocation, how should I deal with it, I mean do I just check if its NULL in a while loop and just keep looping the realloc until its not? – xihtyM Jul 25 '22 at 21:39
  • How you choose to fail is up to you. "Gracefully" springs to mind. Spin looping on a repeated failure is... not graceful, and will most-assuredly *not* work. Report the error, gracefully shutdown if warranted, etc. The important part is you still have the original pointer value left over if you still need it for that workflow. `p = realloc(p,...);` will blast over `p` with NULL on failure, and now you not only failed your realloc, you also lost your original pointer too. – WhozCraig Jul 25 '22 at 21:41
  • @WhozCraig sorry, I just saw your comment. Ok, I didn't know that (My compiler kinda told me at some point to cast it and I just have ever since) about casting memory allocation results... I'll fix that. Thanks. (Also the comment above refers to your second point). – xihtyM Jul 25 '22 at 21:43
  • ok, so i check if it is not null and if it is i exit the program with an error message or something (i am sorry if i am being really dumb right now) – xihtyM Jul 25 '22 at 21:45
  • or, i could do `if (__arr.contents != NULL) { __arr.contents[__arr.size] = to_add; } else { return __a; }` which might work (idk) – xihtyM Jul 25 '22 at 21:46

0 Answers0