странная ошибка памяти в C - PullRequest
       32

странная ошибка памяти в C

1 голос
/ 09 сентября 2011

Я пишу программу на C, которая проверяет наличие круглых символических ссылок.Стратегия заключается в создании struct fileInfo:

typedef struct fileInfo fileInfo;

struct fileInfo {
    ino_t inode;
    dev_t devID;
};

, в которой будут храниться файл inode и devID.Мы создаем массив этих структур и проверяем каждый раз перед открытием нового файла, существует ли файл.Если это так, то это круговая ссылка.

void func1(...)
{

    fileInfo **fileData = malloc(sizeof(struct fileInfo*));
    int fileDataLen = 0;
    char* path = "path of file";
    /* some flags */

    func2(path, fileData, &fileDataLen);

    for (int i = 0; i < fileDataLen; i++)
        free(fileData[i]);
    free(fileData);
}

void func2(char* path, fileInfo ** fileData, int * fileDataLen)
{
    //try to open file
     struct stat buf;
     if (openFile(file, &buf, followSymLinks) == -1)
         exit(1);

     fileData = checkForLoops(fileData, fileDataLen, &buf, file);

     if (S_ISDIR(buf.st_mode)) 
     {
         char* newPath = /* modify path */
         func2(newPath,fileData, fileDataLen);
     }

    /* other stuff */

}

int openFile(char* file, struct stat * buf, fileInfo ** fileData, int * fileDataLen)
{

     if (lstat(path, buf) < 0) 
        {
            fprintf(stderr, "lstat(%s) failed\n", path);
            return -1;
        }
     return 0;
}

fileInfo** checkForLoops(fileInfo **fileData, int * fileDataLen,struct stat *buf, 
                    char* path)
{
    for (int i = 0; i < (*fileDataLen); i++)
    {
        if (fileData[i]->inode == buf->st_ino && 
            fileData[i]->devID == buf->st_dev)
            fprintf(stderr, "circular symbolic link at %s\n", path);
    }


    fileInfo *currFile = malloc(sizeof(struct fileInfo));
    memcpy(&currFile->inode, &buf->st_ino, sizeof(buf->st_ino));
    memcpy(&currFile->devID, &buf->st_dev, sizeof(buf->st_dev));

    fileData[(*fileDataLen)] = currFile;
    (*fileDataLen)++;
    fileData = realloc(fileData, ((*fileDataLen)+1) * sizeof(struct fileInfo*));

    return fileData;
}

Однако я замечаю, что после нескольких вызовов func2 () происходит утечка памяти и fileData указывает на ничто.Я просто не уверен, откуда исходит утечка, так как я ничего не освобождаю в func2 ().Я предполагаю, что есть некоторые realloc махинации, но я не понимаю почему.Помощь будет принята с благодарностью!

Ответы [ 3 ]

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

Я заметил пару странностей в коде.

Во-первых, сигнатура функции openFile имеет тип возврата void, но вы проверяете возвращаемое значение здесь:

if (openFile(file, &buf, fileData, fileDataLen) < 0)

Во-вторых, как Петр также указываетout, вы не выделяете достаточно места при вызове realloc:

fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*));

На первой итерации, где (*fileDataLen) == 0, после увеличения *fileDataLen у вас теперь есть только значение 1Это означает, что вы ничего не перераспределяете (т.е. вы просто возвращаете память, на которую уже указывал fileData, поскольку она не изменила размер выделенного массива).Поэтому в следующий раз, когда вы вызовете fileData[(*fileDataLen)] = currFile; во время другого рекурсивного вызова, вы скопируете значение currFile в fileData[1], но эта память еще не была выделена.Кроме того, при следующем вызове realloc он больше не сможет перераспределять память в том же месте, поэтому fileData будет указывать на совершенно другое место, с копированием только первой записи массива.

В-третьих, вы не можете вызвать free(fileData) в func1(), поскольку вы, вызвав realloc внутри своей функции func2(), изменили значение того, куда указывает память, и вы не передаете фактическую памятьадрес исходной переменной fileData по ссылке на вашу функцию func2().Другими словами, если вызов malloc() в func1() вернул значение, скажем, 0x10000, и вы вызвали realloc для этой выделенной памяти где-то еще в коде, память, которая была выделена в 0x10000, теперь переместилась куда-то еще, но значение fileData в контексте локальной области действия func1() по-прежнему равно 0x10000.Таким образом, когда вы эффективно вызываете free(0x10000), что происходит при вызове free(fileData), вы получите ошибку, так как память для массива больше не выделяется в 0x10000.Чтобы освободить массив, вам либо придется возвращать обновленный указатель на массив указателей из всех рекурсивных вызовов func2(), либо передавать fileData по ссылке, что означает сигнатуру функций func2() и openFile() нужно будет изменить на тип fileInfo***, и вам также понадобится дополнительный уровень косвенности при доступе к fileData в func2() и openFile().Затем, когда вы вызываете realloc где-либо еще, вы фактически изменяете значение fileData, как оно было также выделено в func1(), и можете вызывать free() для этого указателя.

Наконец, продолжайтепомните, что если вы освобождаете только память, выделенную для fileData, у вас будет большая утечка памяти для всех выделенных fileInfo узлов в куче, поскольку fileData был только массивом указателей на узлы, а несами фактические узлы.

1 голос
/ 09 сентября 2011

Ваша проблема в том, что вы не выделяете достаточно памяти для fileData:

fileInfo * fileData = malloc (sizeof (struct fileInfo ));

Здесь вы только выделяете память для одиночного указателя на fileInfo, вместо массива fileInfo экземпляра, который вы, похоже, используете.

Извините, моя первая идея была неверной ... но ваша проблема все еще в том, что вы не выделяете достаточно памяти для fileData - просто в другом месте:

fileData[(*fileDataLen)] = currFile; // 1
(*fileDataLen)++;
fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*)); // 2

Здесь вы выделяете на один элемент меньше, чем нужно. Вы начинаете с fileDataLen из 0 и fileData, содержащего 1 элемент. После открытия первого файла вы увеличиваете fileDataLen до 1, а затем перераспределяете массив, чтобы он содержал 1 элемент вместо 2! Таким образом, при открытии 2-го файла ваш буфер переполняется на // 1 и выше, а часть памяти перезаписывается.

Вы должны всегда сохранять этот инвариант, перераспределяя массив до размера fileDataLen + 1:

fileData = realloc(fileData, (*fileDataLen + 1) * sizeof(struct fileInfo*));
0 голосов
/ 09 сентября 2011

Я не знаю, какую обработку вы выполняете над переменной пути в func2, но вы, возможно, пытаетесь изменить статическую строку, что приведет вас к другой проблеме с памятью, поскольку строки такого типа хранятся вЗона частной памяти, зарезервированная ОС.

...