Возвращаем массив из функции C ++ - PullRequest
0 голосов
/ 10 сентября 2011

Я новичок в C ++, у меня есть опыт работы с C #, Objective-C и JavaScript.

В данный момент я пытаюсь написать функцию, которая принимает путь и возвращает список каталогов (все файлы и папки по этому пути). Я делаю это на Ubuntu.

Вот мой код, честно говоря, я изо всех сил пытаюсь понять синтаксис двойного указателя и то, чего он добивается, но это то, к чему меня привел мой поиск в Google ...

int FileManager::GetDirectoryListing(char *path, dirent **directoryEntries)
{
    // Debug output...
    printf("Listing directory at %s\n", path);

    // Allocate memory for the directory entries
    *directoryEntries = new dirent[MAX_FILES];

    // Open the path we were provided
    DIR *directory = opendir(path);

    // A counter of how many entries we have read
    int entryCount = 0;

    // Make sure we were able to open the directory
    if(directory) {

        printf("Successfully opened directory\n");

        // Read the first entry in the directory
        struct dirent *directoryEntry = readdir(directory);

        // While we have a directory entry
        while(directoryEntry) {

            // Debug output...
            printf("%s\n", directoryEntry->d_name);

            // Copy the directory entry to the array of directory entries we will return
            memcpy(&directoryEntries[entryCount], directoryEntry, sizeof(struct dirent));

            // Increase our counter
            ++entryCount;

            // Read the next directory
            directoryEntry = readdir(directory);
        }

        // Close the directory
        closedir(directory);
    }

    return entryCount;
}

А потом я вызываю эту функцию:

    dirent *directoryEntries = NULL;

    int numberOfEntries = FileManager::GetDirectoryListing(deviceRootPath, &directoryEntries);

    printf("File Manager returned directory listing.\n");

    for(int i = 0; i < numberOfEntries; ++i) {

        printf("Looping through directory entries, at index: %i\n", i);

        printf("%s\n", directoryEntries[i].d_name);
    }

Он блокируется, когда пытается получить доступ к первому элементу в directoryEntries, т.е. в первый раз в цикле.

Я знаю, что не понимаю, что делает двойной указатель, и у меня нет ясного представления о структуре directoryEntries после вызова GetDirectoryListing.

Что происходит и как правильно перебрать directoryEntries?

Ответы [ 3 ]

2 голосов
/ 10 сентября 2011

Эта строка

memcpy(&directoryEntries[entryCount], directoryEntry, sizeof(struct dirent));

должна быть вместо этого:

memcpy(&(*directoryEntries)[entryCount], directoryEntry, sizeof(struct dirent));

или эквивалентно:

memcpy(*directoryEntries + entryCount, directoryEntry, sizeof(struct dirent));

Причина в том, что directoryEntries является указателем на указатель на массив.В памяти это выглядит так:

                                    +------------+
directoryEntries --> array_head --> | dirents[0] |
                                    +------------+
                                    | dirents[1] |
                                    +------------+
                                    | dirents[2] |
                                    +------------+
                                    |    ...     |

Но вы рассматриваете это как directoryEntries - указатель на массив, который не является:

WRONG!               +------------+
directoryEntries --> | dirents[0] |
                     +------------+
                     | dirents[1] |
                     +------------+
                     |    ...     |

Так что выВы записываете за пределы памяти, которой вы не владеете, что приводит к неопределенному поведению.

Причина, по которой вам необходим дополнительный уровень косвенности, заключается в том, что в C параметры функции всегда передаются по значению.Чтобы изменить параметр, вам нужно передать указатель на исходное значение, что вы и делаете.Вам просто нужно помнить, что при работе с этим указателем у вас есть дополнительный уровень косвенности.

Если вы используете C ++, а не C, вам было бы гораздо лучше использовать ссылочный параметр вместо указателя, и вы также должны использовать std::vector<struct dirent>.Вам не нужно беспокоиться о дополнительном уровне косвенности, и управление памятью обрабатывается автоматически.

1 голос
/ 10 сентября 2011
*directoryEntries = new dirent[MAX_FILES];

Что если число каталогов больше MAX_FILES?Откуда вы знаете, что оно не может быть больше MAX_FILES?

Я думаю, вам следует использовать std::vector<dirent> вместо dirent*.Многие проблемы будут решены.

Я бы реализовал функцию следующим образом:

std::vector<dirent> FileManager::GetDirectoryListing(char *path)
{
    std::vector<dirent> dirs;
    DIR *directory = opendir(path);
    if(directory) {
        struct dirent *directoryEntry = readdir(directory);
        while(directoryEntry) {
            dirs.push_back(*directoryEntry); //push a copy of the original!
            directoryEntry = readdir(directory);
        }
        closedir(directory);
    }
    return dirs;
}

Современные компиляторы, скорее всего, оптимизируют этот код, чтобы избежать копирования возвращаемого значения.Эта оптимизация называется:

Также обратите внимание, что directories.size() сообщит вамЧисло входов.Так что на сайте звонков вы можете просто сделать это:

std::vector<dirent> dirs = FileManager::GetDirectoryListing(deviceRootPath);
for(size_t i = 0; i < dirs.size() ; ++i)
{
  std::cout << dirs[i].d_name << std:endl;
}

В общем, предпочитайте std::cout вместо printf, так как последний небезопасен!

0 голосов
/ 10 сентября 2011

Ваша ошибка в этой строке:

        memcpy(&directoryEntries[entryCount], directoryEntry, sizeof(struct dirent));

Ваш directoryEntries является указателем на указатели на struct dirent. Каждая запись в нем является указателем на struct dirent. Ваш '&' заставляет вас скопировать в адрес указателя, а это не то, что вы хотели. Вы хотите:

        memcpy(directoryEntries[entryCount], directoryEntry, sizeof(struct dirent));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...