-4

I'm using the dirent.h library to scan a directory for all the files it contains and store pointers to the resulting objects in an array. I've been following this old SO question describing the storage of structure pointers in arrays, but I'm running into issues during my implementation.

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

int main(int argc, char **argv) 
{
    DIR *d = NULL;
    struct dirent *dir = NULL;
    struct dirent *d_array[50]; //big overkill array to avoid realloc
    size_t n = 0; //number of items

    d = opendir("scandir"); //name of directory to search
    if(d)
    { 
        while((dir = readdir(d))!=NULL) {
            //d_array[n] = malloc(sizeof(struct dirent));
            d_array[n++] = dir;
        }
        closedir(d);
    }
    for(size_t i = 0;i<n;i++) {
        printf(d_array[n]->d_name);
        free(d_array[n]);
    }
    free(d_array);
    return 0;
}

Running the above code results in a Segmentation fault: 11. I thought this was probably because I was properly allocating the memory for the structures (as seen in the commented out malloc), but including that gives the following error:

error: assigning to 'struct dirent *' from incompatible type
  'void *'

I don't understand why d_array[n] = malloc(sizeof(struct dirent)), which is verbatim from multiple posts about this topic, is having this incompatible type error. And if it's inappropriate to use, why am I getting a segfault?

Hierophect
  • 327
  • 1
  • 2
  • 15
  • How are you compiling this code? What command do you use? – melpomene Dec 05 '18 at 18:11
  • Are you compiling this with a C++ compiler? A conforming C compiler should not complain about assigning the return value of `malloc()` to an object of any object pointer type. – John Bollinger Dec 05 '18 at 18:18
  • @xing good catch, but does not solve allocation error. New error is `a.out(3319,0x7fff74201300) malloc: *** error for object 0x7fb772003218: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug ...Abort trap: 6` – Hierophect Dec 05 '18 at 18:37

1 Answers1

0

Running the above code results in a Segmentation fault: 11.

This could be because of

    printf(d_array[n]->d_name);

or

    free(d_array[n]);

, because n is the number of directory entries, and d_array[n] is uninitialized. (You appear to have meant d_array[i]) Note, too, that you should not attempt to free the pointer returned by readdir(), as it does not belong to you. The documentation specifically says that you should not attempt to free it. That would potentially apply in the version in which you just assign the pointer itself to the array.

It could also be because of

free(d_array);

, since d_array itself is not dynamically allocated. You could reasonably hope that your compiler would warn about that one. Mine does.

I don't understand why d_array[n] = malloc(sizeof(struct dirent)), which is verbatim from multiple posts about this topic, is having this incompatible type error.

That would be because your compiler is non-conforming. Perhaps you are using a C++ compiler instead of a C compiler -- these are not interchangeable. That statement is perfectly valid C, given the declarations that are in scope where it appears in your code.

Addendum: As for what you actually should do, if you are going to dynamically allocate memory for the elements of d_array to point to (and you should, if you use an array of pointers at all) then you need to copy the pointed-to struct dirent structures into the allocated space, not assign the returned pointers directly to the array. That would be

    *d_array[i] = *dir;

as Paul Ogilvie first pointed out in a now-deleted answer.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks for the answer. I fixed the index issue, but unfortunately, it is not solving my allocation problem, even when forcing the compiler to use c. I get the following error now: `a.out(3319,0x7fff74201300) malloc: *** error for object 0x7fb772003218: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug ...Abort trap: 6` – Hierophect Dec 05 '18 at 18:40
  • 1
    @Hierophect, the indexing issue is only one of several I pointed out. Some of the others involve pointers that indeed you should not attempt to free. However, there are one or two more things I perhaps should mention. – John Bollinger Dec 05 '18 at 18:42
  • Additionally, while I can force the program to compile in C now, I would ideally like to include this code in a C++ program (due to the requirements of other libraries). Is there another way to avoid this error? – Hierophect Dec 05 '18 at 18:42
  • @Hierophect The whole structure of this code is broken. If you're using C++, you should just read the entry names into a `vector` and forget about copying `struct dirent`s around. – melpomene Dec 05 '18 at 18:43
  • The `readdir` man page explicitly says you can't copy `struct dirent`s like that: "*POSIX.1 explicitly notes that [d_name] should not be used as an lvalue. [...] (On some systems, this field is defined as `char d_name[1]`!)*" – melpomene Dec 05 '18 at 18:47
  • @melpomene, well, be that as it may, now that I've fixed my index error, deleted the erroneous free statements, and continued to comment out the malloc... it works. Should I be worried about leaving it as is? This is embedded systems firmware so I avoid vectors wherever possible. – Hierophect Dec 05 '18 at 18:47
  • @Hierophect, you tagged your question [c]. Kudos on *not* tagging both [c] and [c++], which is almost never correct, but if you want a C++ solution then that would be an altogether different question and answer. – John Bollinger Dec 05 '18 at 18:48
  • @Hierophect Then use `strdup(dir->d_name)` or whatever. But don't mess with `struct dirent`s. – melpomene Dec 05 '18 at 18:48
  • I apologize - I did not realize this would end up being a compiler compatibility issue. – Hierophect Dec 05 '18 at 18:49
  • @Hierophect, there is no a "compiler compatibility" issue here. There is a "what language are you programming in?" issue. C and C++ are different languages, with some overlap. – John Bollinger Dec 05 '18 at 18:50
  • @melpomene, I don't see that text about using `d_name` as an lvalue in [the current version of POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html), and it does not seem compatible with the existence of `readdir_r()` (because how, then, could the user create an appropriate buffer into which that function could write directory entries?). – John Bollinger Dec 05 '18 at 18:54
  • @JohnBollinger As for `readdir_r`: "*The storage pointed to by `entry` shall be large enough for a `dirent` with an array of `char d_name` members containing at least {NAME_MAX}+1 elements.*" I read this as a requirement on the application programmer to make sure they allocate at least `sizeof (struct dirent) + NAME_MAX + 1` bytes for `entry`. – melpomene Dec 05 '18 at 19:00
  • @JohnBollinger referring to your Addendum, and comment on C/C++. I tend to program as close to C as possible but I typically have to use a C++ compiler because of the platforms I work on. I'm not 100% versed on the cases where C is not a subset of C++, so I missed this issue. Would you describe your suggested solution as a decent one, or would you recommend I seek a C++ solution given the circumstances? – Hierophect Dec 05 '18 at 19:15
  • @Hierophect, the answer to "What language are you programming in?" can be "the common subset of C and C++", but that's a tricky exercise. If your circumstances require you to use a C++ compiler, then I say embrace it. Use natural C++ idioms, even if you don't go wild with making a class for everything. – John Bollinger Dec 05 '18 at 19:24
  • I will try Vector then - my more recent research suggests my universal avoidance of it on microprocessors may be misplaced anyway. I appreciate your polite responses. – Hierophect Dec 05 '18 at 19:30