2

I am using dirent to read filenames from a specific folder, and I want to save the names in a char* vector. It seems that it's copying some weird symbols instead of copying the filenames. This is what I tried so far:

std::vector<char*> filenames;

int filenamesAndNumberOfFiles(char *dir)
{
    struct dirent *dp;
    DIR *fd;
    int count = 0;

    if ((fd = opendir(dir)) == NULL) 
    {
        fprintf(stderr, "listdir: can't open %s\n", dir);
        return 0;
    }
    while ((dp = readdir(fd)) != NULL) 
    {
        if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
            continue;    /* skip self and parent */
        printf("Filename: %s\n", dp->d_name);
        filenames.push_back(dp->d_name);
        count++;
    }
    closedir(fd);
    return count;
}

Can anyone tell me why it doesn't copy the filenames and how could I do to copy them?

Edit: d_name is a char variable declared as:

char d_name[PATH_MAX];

and it seems like in my program PATH_MAX is equal to 260.

PS: It is my first time when I use dirent, so I am not really familiar with it.

CristianLuca
  • 111
  • 2
  • 10

4 Answers4

6

Replace std::vector<char*> with std::vector<std::string>. And add #include <string>. That should solve the problem.

It seems that you haven't yet grasped the concept of pointers and arrays in C/C++, and I'm not sure if explaining that fits within the limits of an SO answer. But this answer might be of use: C++ : Does char pointer to std::string conversion copy the content?

In short, the problem with your code is that instead of copying the memory where string is stored (the characters themselves, char[PATH_MAX]), you were only copying a pointer to that memory (char*). And the memory block pointed to was later reused or even deleted, leaving your stored pointer invalid.

Community
  • 1
  • 1
Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • Or he can put at the top of the file `using namespace std;` and then write `vector` – Alex Jan 13 '16 at 09:31
  • 3
    @Alex No, you don't : http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice – Hatted Rooster Jan 13 '16 at 09:32
  • 1
    @Alex: Or he can better avoid it. – Pixelchemist Jan 13 '16 at 09:32
  • Ok ok wow ! I just learnt something. I just tried to help but as C is not my main language at all, I didn't know it could be wrong to use namespace – Alex Jan 13 '16 at 09:36
  • It's ok to use namespace, but when you use strings you should declare them as `std::string` otherwise it can became ambiguous. – CristianLuca Jan 13 '16 at 09:48
  • 1
    @Alex sorry if I am too pedantic, but C is not C++, and this question is about C++. C does not even have namespaces. – eerorika Jan 13 '16 at 09:51
  • @user2079303 ok well :) it's always a good thing to spread the word ! And to learn the good things – Alex Jan 13 '16 at 10:04
3

You need to store copies of the string. Use vector<string>. When you push_back(dp->d_name) you are storing a dangling pointer, because dp goes out of scope after the function ends.

Simple
  • 13,992
  • 2
  • 47
  • 47
  • Put some code for him and this will be the best answer. – BlueTrin Jan 13 '16 at 09:35
  • `dp` going out of scope does not cause the char pointer to dangle. The problem is calling `readdir` repeatedly which causes the previous string to be freed and therefore leaves the pointer dangling. The solution is correct, though. – eerorika Jan 13 '16 at 09:41
3

This is because you push a pointer, dp->d_name onto the vector, however the string this pointer points to is gone when you call the next readdir() call.

Instead, you must make a copy of that string, and push it onto the vector:

filenames.push_back(strdup(dp->d_name));

Now, you must remember to free() this string that is copied into the vector when you are done with your filenames vector

However, please don't do this at all. Just use:

std::vector<std::string> filenames;

And you can use your original code here, and the memory management will be taken care of.

nos
  • 223,662
  • 58
  • 417
  • 506
1

As user2079303 said a second call to readdir will overwrite the results returned by the first call (link).

But I would recommend you to use a string for this in C++:

std::vector<std::string> filenames;

int filenamesAndNumberOfFiles(char *dir)
{
    struct dirent *dp;
    DIR *fd;
    int count = 0;

    if ((fd = opendir(dir)) == NULL) 
    {
        fprintf(stderr, "listdir: can't open %s\n", dir);
        return 0;
    }
    while ((dp = readdir(fd)) != NULL) 
    {
        if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
            continue;    /* skip self and parent */
        printf("Filename: %s\n", dp->d_name);
        filenames.push_back( std::string(dp->d_name) );
        count++;
    }
    closedir(fd);
    return count;
}

You can always use c_str() if you need a char* later on.

BlueTrin
  • 9,610
  • 12
  • 49
  • 78
  • 1
    As I commented on Simple's answer, the reason why you claim the string becomes invalid is wrong. `dp` is just a pointer and it going out of scope has no effect on the string. – eerorika Jan 13 '16 at 09:56