0

My problem is that it fails to assign memory to a (char*) pointer and I can't seem to understand why. I reviewed other similar questions, but I can't seem to grasp the answer. I'm trying to return a list of strings, but I know that in C I can't return char**, but I can do that via the char** parameter. My attempt is in the code snippets below.

in a.c

void list(char** fileList) {
    int fileIndex = 0;
    fileList = (char**)malloc(DIRECTORY_SIZE * sizeof(char*));
    while ((dir = readdir(currentDirectory)) != NULL)
    {
       fileList[fileIndex] = (char*)malloc(FILENAME_LENGTH * sizeof(char)); //It fails here
       fileList[fileIndex] = strncpy(fileList[fileIndex], dir->d_name, FILENAME_LENGTH);
       fileIndex++;
       printf("%s\n", fileList[fileIndex]);
    }
}

in main.c

int main() {
    char** fileList;
    list(fileList);
    for(int i = 0; i < 10; i++) {
        printf("%s\n", fileList[i]);
    }
}

I already looked over these questions:

2eety
  • 89
  • 1
  • 13
  • 1
    The function deals with a copy of the value of the argument expression. Changing the copy does not influence on the original argument. So within main you have the uninitialized pointer char** fileList; that still stays as such after calling the function. – Vlad from Moscow Jan 07 '21 at 09:56
  • 1
    "...but I know that in C I can't return char**" Who told you that? You *can* return a `char**` value - and, in your case, it would make sense to. – Adrian Mole Jan 07 '21 at 09:59
  • @VladfromMoscow, yeah that makes sense, but I still can't allocate memory inside the function. – 2eety Jan 07 '21 at 10:04
  • @AdrianMole I thought you can't, but still I can't allocate memory inside the function. It fails with Segamentation fault on the line marked by the comment. – 2eety Jan 07 '21 at 10:09
  • 1
    What if you change the `while` condition test to `while (fileIndex < DIRECTORY_SIZE && (dir = readdir(currentDirectory)) != NULL)`? – Ian Abbott Jan 07 '21 at 10:22
  • @IanAbbott it still fails, I forgot to mention that DIRECTORY_SIZE = 100. I'm aware of the issue that if the fileIndex is over DIRECTORY_SIZE there should be an error(not enough memory or something, I'm not sure about the exact error), but it fails on the first memory allocation. – 2eety Jan 07 '21 at 10:28
  • 1
    The `fileIndex++;` and `printf("%s\n", fileList[fileIndex]);` statements are the wrong way round in the `while` loop, so it is trying to print a string located by a junk pointer value. Also, if any of the `dir->d_name` strings are at least as long as `FILENAME_LENGTH`, `strncpy(fileList[fileIndex], dir->d_name, FILENAME_LENGTH);` will not append a null terminator. – Ian Abbott Jan 07 '21 at 10:43
  • I thought all this time that the fault was at the memory management, since there it seemed to fail. Thanks a lot! – 2eety Jan 07 '21 at 10:53

1 Answers1

2

What happens is that the memory pointer fileList gets copied to the list function, when the list function then changes the pointer, but the initial pointer wouldn't change.

To fix this you could give it a pointer to the pointer to change the actual pointer value, like this:

void list(char*** fileList) {
    int fileIndex = 0;
    *fileList = (char**)malloc(DIRECTORY_SIZE * sizeof(char*));
    while ((dir = readdir(currentDirectory)) != NULL)
    {
       *fileList[fileIndex] = (char*)malloc(FILENAME_LENGTH * sizeof(char)); //It fails here
       *fileList[fileIndex] = strncpy(fileList[fileIndex], dir->d_name, FILENAME_LENGTH);
       fileIndex++;
       printf("%s\n", fileList[fileIndex]);
    }
}

int main() {
    char** fileList;
    list(&fileList);
    for(int i = 0; i < 10; i++) {
        printf("%s\n", fileList[i]);
    }
}

But I personally find this a bit clunky, so it would be better to do it like this:

char** list(void) {
    char** fileList;
    int fileIndex = 0;
    fileList = (char**)malloc(DIRECTORY_SIZE * sizeof(char*));
    while ((dir = readdir(currentDirectory)) != NULL)
    {
       fileList[fileIndex] = (char*)malloc(FILENAME_LENGTH * sizeof(char)); //It fails here
       fileList[fileIndex] = strncpy(fileList[fileIndex], dir->d_name, FILENAME_LENGTH);
       fileIndex++;
       printf("%s\n", fileList[fileIndex]);
    }
    return filelist;
}

int main() {
    char** fileList = list();
    for(int i = 0; i < 10; i++) {
        printf("%s\n", fileList[i]);
    }
}
  • `char** list()` should be `char** list( void )`. `char** list()` is a function that takes an indeterminate number of arguments. `char** list( void )` is a function that takes no arguments. They're not the same thing. – Andrew Henle Jan 07 '21 at 13:47
  • I didn't know that. I don't often use C, mostly C++ and I don't think that's a thing there. –  Jan 07 '21 at 14:23
  • It's not - in C++ `char** list()` is the same as `char** list( void )`. It's related to prototypes being mandatory in C++ and (originally) optional in C. – Andrew Henle Jan 07 '21 at 14:53
  • Yeah, that makes sense. –  Jan 08 '21 at 19:24