Ошибка сегментации после realloc(). Не удается назначить выделенную память указателю

Я пытаюсь выделить память с помощью realloc(). Это работает до сих пор. Но если я хочу присвоить выделенную память указателю в структурной переменной, я получаю ошибку сегментации:

// in header
typedef struct {
    int a;
    char test[20];
} MyContent;

typedef struct {
    MyContent* values; 
    // simmilar to: MyContent values[]
    // ... some other stuff
} MyData;

// in source
void myFunction(MyData* dataPtr) {
    dataPtr->values = NULL;
    MyData* tempPtr = NULL;

    for (int i = 1; i < 10; i++) {
        tempPtr = (MyContent*) realloc(dataPtr->values, i * sizeof(MyContent));
        if (tempPtr == NULL) {
            free(dataPtr->values);
            break;
        }
        dataPtr->values = tempPtr;  // Here I get the segmentation fault
        dataPtr->values[(i-1)].a = 42;
        // ...
    }
}

Я не могу понять, что здесь происходит. Какие-либо предложения? Спасибо за вашу помощь.


person fondor    schedule 25.04.2012    source источник
comment
Ошибка в коде, который вы не вставили. Приведенный выше код имеет только одну существенную проблему — он неправильно обрабатывает случай, когда realloc возвращает NULL. Если вы можете опубликовать полный компилируемый пример, показывающий ошибку, мы, вероятно, сможем найти его для вас. В противном случае запустите valgrind в своем коде. (Кстати, это код C или C++? Вы ставите оба тега, и это очень запутывает.)   -  person David Schwartz    schedule 25.04.2012
comment
@DavidSchwartz, код, опубликованный OP, отлично скомпилируется и запустится под C или C ++ :)   -  person bdonlan    schedule 25.04.2012
comment
@bdonlan: Правильно, так что невозможно понять, о чем он спрашивает. Если бы я собирался протестировать его, например, должен ли я тестировать его как код C или код C++? Если бы я собирался предложить исправления/изменения, должны ли они быть кодом C или C++?   -  person David Schwartz    schedule 25.04.2012
comment
@DavidSchwartz, вы можете ограничиться пересечением C и C ++, что в основном составляет большую часть C (с некоторыми незначительными отличиями). Было бы полезно точно знать, какой ОП использует, но не невозможно попытаться помочь, не зная об этом. Только не предполагайте void* автоприведения и не называйте свои переменные class :)   -  person bdonlan    schedule 25.04.2012
comment
@user1355415 user1355415, я думаю, что ваше последнее редактирование на самом деле усугубляет ситуацию - ваши приведения теперь соответствуют размеру, но вы назначаете dataPtr->values, который по-прежнему равен MyContent*...   -  person bdonlan    schedule 25.04.2012
comment
Спасибо, извините за путаницу. Сначала поправил нужное. Надеюсь теперь подойдет. Кстати, это (по крайней мере, должно быть) код C.   -  person fondor    schedule 25.04.2012
comment
Вопрос не по теме. Можно ли посмотреть историю изменений поста?   -  person tuxuday    schedule 25.04.2012
comment
@tuxuday, щелкните метку времени после «отредактировано» выше. например, stackoverflow.com/posts/10310813/revisions   -  person bdonlan    schedule 25.04.2012
comment
@ user1355415, вы продолжаете вносить тонкие изменения, которые могут повлиять или не повлиять на проблемы в отредактированном коде. На данный момент я не думаю, что мы можем сделать какие-либо выводы о том, что может делать ваш настоящий код. Попробуйте опубликовать настоящий код или просто запустите на нем valgrind и посмотрите, что вы можете найти самостоятельно.   -  person bdonlan    schedule 25.04.2012
comment
спасибо @dbdonlan. Проблема в том, что tempPtr — это MyData, тогда как он должен быть MyContent. Как только вы сделаете это изменение, остальные должны встать на свои места автоматически.   -  person tuxuday    schedule 25.04.2012


Ответы (3)


Похоже, вы отредактировали свой код. Отредактированный код работает нормально.

#include<stdio.h>
#include<malloc.h>
#include<string.h>
// in header
typedef struct {
    int a;
    char test[20];
} MyContent;

typedef struct {
    MyContent* values; 
    // simmilar to: MyContent values[]
    // ... some other stuff
} MyData;

// in source
void myFunction(MyData* dataPtr) {
    dataPtr->values = NULL;
    MyData* tempPtr;

    for (int i = 1; i < 10; i++) {
        tempPtr = (MyData*) realloc(dataPtr->values, i * sizeof(MyContent));
        if (tempPtr == NULL) {
            if(dataPtr->values)
                free(dataPtr->values);
            printf("realloc() failed\n");
            return ;
        }
        dataPtr->values = (MyContent*)tempPtr;  // Here I get the segmentation fault
        dataPtr->values[(i-1)].a = 42+i;
        strcpy(dataPtr->values[(i-1)].test,"name");
    }
}

void PrintData(MyData* dataPtr) {
    for (int i = 1; i < 10; i++)
        printf("We have %s at %d\n",dataPtr->values[(i-1)].test,dataPtr->values[(i-1)].a);
}

main() {
    MyData Sample;
    myFunction(&Sample);
    PrintData(&Sample);
}
person tuxuday    schedule 25.04.2012
comment
Извините за изменения в коде во время всех ваших ответов. Попробовал стянуть все лишнее, чтобы разобраться в проблеме. При этом я мог что-то упустить или изменить не так. Я понял, что аргумент, который я передал myFunction(), был ошибочным, что я теперь понял из-за вашего примера сообщения здесь. Спасибо всем - отличная и супер быстрая поддержка. - person fondor; 25.04.2012

На первый взгляд, я не вижу проблемы, которая могла бы привести к сбою, - эта адресация на основе единиц немного странная, но не неправильная. В коде может быть проблема, которую вы не показываете, что приводит к повреждению кучи или стека, что усугубляется вызовом realloc. Или, если вы компилируете с оптимизацией, ваш отладчик может не понять, где на самом деле происходит сбой. Вы также путаете MyData и MyContent, но я предполагаю, что это только потому, что вы допустили ошибку при редактировании кода.

Также обратите внимание, что если realloc завершится ошибкой, произойдет сбой в строке после той, которую вы указали, так как вы будете писать в нулевой указатель. Вам нужно прервать, если tempPtr is NULL, а не просто освободить старый указатель. Опять же, это вызывает ошибку на другой линии, чем вы указали.

Я бы порекомендовал запустить вашу программу под управлением valgrind, чтобы увидеть, где она сообщает об ошибках - виновником, скорее всего, будет первая такая ошибка.

person bdonlan    schedule 25.04.2012
comment
Да, я перепутал MyData и MyContent в операторе realloc(). Извините за это, я исправил это. Я также пропустил break, что вызвало дополнительную путаницу. - person fondor; 25.04.2012

Ваше условие неверно для проверки вновь выделенной памяти. Должен быть:

if (tempPtr == NULL) {
  // handle error condition or continue with original 'dataPtr->values'
}
else {
  dataPtr->values = tempPtr;
}

Помните, что realloc() не обязательно переводит один блок в другой блок. Иногда он может выделять память в той же области указателя.

person iammilind    schedule 25.04.2012
comment
Это неправильно. Вы не должны освобождать старый указатель, если он изменился; realloc уже позаботился об этом за вас. - person bdonlan; 25.04.2012
comment
Функция reallocate сама освободит старый блок. Вы не можете освободить его. - person David Schwartz; 25.04.2012
comment
Спасибо, не знал, что realloc() frees сам старый блок. - person fondor; 25.04.2012