C отказ сегментации перед/во время оператором возврата

Я печатаю значение, которое я возвращаю прямо перед моим оператором возврата и говорю моему коду печатать значение, которое было возвращено прямо после вызова функции. Однако я получаю отказ сегментации после своего первого оператора печати и перед моей секундой (также интересный отметить, это всегда происходит на третьем разе, когда функция вызвана; никогда первое или второе, никогда четвертое или позже). Я пытался распечатать все данные, которые я продолжаю работать, чтобы видеть, делала ли остальная часть моего кода что-то, что это, возможно, не было должно, но мои данные до той точки выглядят хорошо. Вот функция:

int findHydrogen(struct Amino* amino, int nPos, float* diff, int totRead) {

    struct Atom* atoms;
    int* bonds;
    int numBonds;
    int i;
    int retVal;
    int numAtoms;

    numAtoms = (*amino).numAtoms;

    atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms);
    atoms = (*amino).atoms;

    numBonds = atoms[nPos].numBonds;

    bonds = (int *) malloc(sizeof(int) * numBonds);
    bonds = atoms[nPos].bonds;

    for(i = 0; i < (*amino).numAtoms; i++)
        printf("ATOM\t\t%d  %s\t0001\t%f\t%f\t%f\n", i + 1, atoms[i].type, atoms[i].x, atoms[i].y, atoms[i].z);

    for(i = 0; i < numBonds; i++) 
        if(atoms[bonds[i] - totRead].type[0] == 'H') {
            diff[0] = atoms[bonds[i] - totRead].x - atoms[nPos].x;
            diff[1] = atoms[bonds[i] - totRead].y - atoms[nPos].y;
            diff[2] = atoms[bonds[i] - totRead].z - atoms[nPos].z;

            retVal = bonds[i] - totRead;

            bonds = (int *) malloc(sizeof(int));
            free(bonds);

            atoms = (struct Atom *) malloc(sizeof(struct Atom));
            free(atoms);

            printf("2 %d\n", retVal);

            return retVal;
        }
}

Как я упомянул прежде, это хорошо работает первые два раза, я выполняю его, в третий раз, когда это печатает правильное значение retVal, затем seg отказы где-нибудь, прежде чем это доберется туда, где я вызвал функцию, которую я делаю как:

hPos = findHydrogen((&aminoAcid[i]), nPos, diff, totRead);
printf("%d\n", hPos);
6
задан Pascal Cuoq 15 June 2010 в 20:12
поделиться

10 ответов

Из этого кода нелегко догадаться, где ошибка (потенциально ошибка может быть почти в каждой строке кода) - скорее всего, где-то произошел выход за пределы буфера, однако если вы работаете на *nix, запустите свою программу под valgrind, вы сможете найти ошибку довольно быстро.

Эти строки выглядят странно:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms);
atoms = (*amino).atoms;

У вас утечка памяти, поскольку вы отбрасываете указатель, возвращенный malloc. То же самое с bonds, и то же самое снова внутри цикла for.

8
ответ дан 8 December 2019 в 02:16
поделиться

РЕДАКТИРОВАТЬ Что ж, у вас утечка памяти направо и налево, но не совсем так, как я думал. Фиксированная последовательность ниже:

В частности, когда вы это делаете:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); // 1
atoms = (*amino).atoms; // 2
// ...
atoms = (struct Atom *) malloc(sizeof(struct Atom)); // 3
free(atoms); // 4

Происходит то, что вы выделяете некоторую память и помещаете адрес в атомов на этапе (1). Затем вы отбрасываете этот адрес и вместо этого указываете атомов на часть внутренней части вашей амино структуры в (2). Затем вы выделяете второй указатель с одним атомом. Наконец, вы можете позвонить по бесплатному телефону . Таким же образом обрабатываются облигации . Вы, вероятно, имеете в виду что-то вроде этого:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); // 1
memcpy(atoms, (*amino).atoms, sizeof(struct Atom) * numAtoms); // 2
// ...
// delete 3
free(atoms); // 4

Обратите внимание, что если у Atom есть какие-либо компоненты-указатели, вы можете захотеть выполнить цикл for и скопировать атомы по отдельности вместе с их содержимым, которое затем вам придется индивидуально бесплатно в точке возврата.

... или, может быть, просто это, если вы хотите только прочитать данные атомов из структуры:

atoms = (*amino).atoms; // delete 1, 3, 4 entirely and just read directly from the structure

Другие ответы, в которых говорится о количестве места в diff и других вопросах, вероятно, также стоит расследование.

РЕДАКТИРОВАТЬ: исправлена ​​последовательность вызовов в соответствии с образцом кода.

5
ответ дан 8 December 2019 в 02:16
поделиться

Похоже, что вы используете операторы print для отладки ошибок сегментации: большое "нет-нет" в C.

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

Вместо этого следует использовать отладчик типа gdb, который подскажет вам точную строку кода, вызывающую ошибку сегментации.

Если вы не знаете, как использовать gdb, вот краткое руководство, которое я нашел в Google: http://www.cs.cmu.edu/~gilpin/tutorial/

3
ответ дан 8 December 2019 в 02:16
поделиться

РЕДАКТИРОВАТЬ: прочтите это Доступ к значениям массива с помощью арифметики указателя и индексации в C должен помочь вам понять, что такое указатели и массивы

Эти строки не на самом деле имеют какое-либо реальное влияние на то, что делает код, поэтому вы можете их удалить

bonds = (int *) malloc(sizeof(int));
free(bonds);

atoms = (struct Atom *) malloc(sizeof(struct Atom));
free(atoms);

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

numAtoms = (*amino).numAtoms;

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms);
atoms = (*amino).atoms;

numBonds = atoms[nPos].numBonds;

bonds = (int *) malloc(sizeof(int) * numBonds);
bonds = atoms[nPos].bonds;

Вам следует ненадолго прекратить кодирование и убедиться, что вы понимаете указатели, прежде чем делать что-то еще, потому что вы явно этого не делаете, и это просто причинит вам много-много боли, пока вы не сделаете это, однако вот версия вашего код, который должен делать что-то похожее на то, что вы хотите:

int findHydrogen(struct Amino* amino, int nPos, float* diff, int totRead) {

    struct Atom* atoms;
    int* bonds;
    int numBonds;
    int i;
    int retVal;
    int numAtoms = amino->numAtoms;

    numAtoms = amino->numAtoms;
    atoms = amino->atoms;

    numBonds = atoms[nPos].numBonds;
    bonds = atoms[nPos].bonds;

    for(i = 0; i < amino->numAtoms; i++)
        printf("ATOM\t\t%d  %s\t0001\t%f\t%f\t%f\n", i + 1, atoms[i].type, atoms[i].x, atoms[i].y, atoms[i].z);

    for(i = 0; i < numBonds; i++) 
        if(atoms[bonds[i] - totRead].type[0] == 'H') {
            diff[0] = atoms[bonds[i] - totRead].x - atoms[nPos].x;
            diff[1] = atoms[bonds[i] - totRead].y - atoms[nPos].y;
            diff[2] = atoms[bonds[i] - totRead].z - atoms[nPos].z;

            retVal = bonds[i] - totRead;

            printf("2 %d\n", retVal);

            return retVal;
        }
}
3
ответ дан 8 December 2019 в 02:16
поделиться

Есть ли у вас в файле #include ? Мне интересно, получаете ли вы вызов printf(), который использует неявное объявление printf() и поэтому может использовать неправильное соглашение о вызове.

Какой компилятор/платформу вы используете? Получаете ли вы какие-либо предупреждения при сборке?

1
ответ дан 8 December 2019 в 02:16
поделиться

Здесь много чего не так.

Первое, что я заметил, это утечка памяти (вы выделяете немного памяти в (struct Atom *) malloc(sizeof(struct Atom) * numAtoms), затем перезаписываете указатель указателем в аминоструктуре); вы делаете то же самое с (int *) malloc(sizeof(int) * numBonds); .

Во-вторых, вы не проверяете границы выражения bonds[i] - totRead.

В-третьих, и я думаю, что именно здесь у вас происходит сбой, здесь вы перезаписываете указатель на атомы: atoms = (struct Atom *) malloc(sizeof(struct Atom)); free(atoms); что оставляет atoms указателем на недопустимую память.

4
ответ дан 8 December 2019 в 02:16
поделиться

Это странно:

bonds = (int *) malloc(sizeof(int));
free(bonds);

atoms = (struct Atom *) malloc(sizeof(struct Atom));
free(atoms);

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

Эта строка также выглядит опасной:

atoms[bonds[i] - totRead].type[0] == 'H'

Убедитесь, что вы остаетесь внутри массива с вашим индексом.

3
ответ дан 8 December 2019 в 02:16
поделиться

Вот небольшой рерайт части вашего кода для демонстрации утечек памяти:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); // allocating new struct
atoms = (*amino).atoms; // overriding the pointer with pointer to a struct allocated in the caller
//...
for (some counter on atoms)
{
    if (something that succeeds)
    {
      atoms = (struct Atom *) malloc(sizeof(struct Atom)); // overwrite the pointer yet again with newly allocated struct
      free(atoms); // free the last allocated struct
      // at this point atoms points to invalid memory, so on the next iteration of the outer for it'll crash
    }
}

Также есть вероятность, что оператор bonds[i] - totRead может выходить за границы atoms[], что может быть причиной segfault.

4
ответ дан 8 December 2019 в 02:16
поделиться

Там, где вы написали:

/* Allocate new space for a copy of the input parameter "Atoms" */
atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms);
/* Immediately lose track of the pointer to that space, once was stored
   in atoms, now being lost. */
atoms = (*amino).atoms;

Я думаю, что ваше намерение должно быть таким:

/* Allocate new space for a copy of the input parameter "Atoms" */
atoms = (struct Atom *)malloc(sizeof(struct Atom) * numAtoms);

/* Copy the input parameter into the newly-allocated memory. */
for (i = 0; i < numAtoms; i++)
    atoms[i] = (*amino).atoms[i];

что также можно записать как:

/* Allocate new space for a copy of the input parameter "Atoms" */
atoms = (struct Atom *)malloc(sizeof(struct Atom) * numAtoms);

/* Copy the input parameter into the newly-allocated memory. */
memcpy(atoms, (*amino).atoms, sizeof(struct Atom) * numAtoms);

В C нет встроенного оператора equals (=) для копирования массивов, как вы, похоже, хотели. Вместо этого вы теряете указатель на выделенную память, ранее хранившийся в переменной atoms, и начинаете первую итерацию цикла с atoms, указывающим на "входную копию" массива atoms.


Часть проблемы заключается в том, что вы вызываете free на память, но затем продолжаете обращаться к указателю на эту освобожденную память. Вы не должны обращаться к указателям на освобожденную память. Чтобы избежать этого, замените все обращения к free на:
#ifdef free
#    undef free
#endif
#define free(f) freeptr(&f)

void freeptr(void **f)
{
    /* This function intentionally segfaults if passed f==NULL, to alert
       the programmer that an error has been made.  Do not wrap this code
       with a check "if (f==NULL)", fix the problem where it is called. 

       To pass (*f==NULL) is a harmless 'no-op' as per the C standard
       free() function.

       If you must access the original, built-in free(), use (free)() to
       bypass the #define macro replacement of free().

     */

    (free)(*f);  /* free() must accept NULL happilly; this is safe. */
    *f = NULL;   /* Set the pointer to NULL, it cannot be used again. */
}

На данный момент вы можете просто вырезать и вставить приведенный выше код в начало вашей программы. Хорошее место - после последней директивы #include, но это должно происходить на уровне файлов и до первого использования free() в коде.

После перекомпиляции вашего кода вы обнаружите ошибки Bus Errors и Segmentation Violation сразу после free(atom). Это правильно, и цель freeptr() - привести ваш код к немедленному краху, а не к текущей ситуации, когда ваш код неправильно использует указатели и приводит к проблемам, которые очень трудно отладить.

Чтобы окончательно исправить распределение памяти, переставьте строки:

bonds = (int *) malloc(sizeof(int));
free(bonds);

которые должны звучать так:

free(bonds);
bonds = (int *) malloc(sizeof(int));

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


При выделении bonds необходимо выделять память не под одно (1) целое число, а под столько целых чисел, сколько numBonds:
free(bonds);
bonds = (int *) malloc(sizeof(int) * numBonds);

или, что лучше для большинства кодеров на Си:

free(bonds);
/* The calloc function performs the multiplication internally, and
   nicely zero-fills the allocated memory. */
bonds = calloc(numBonds, sizeof(int));

Вам нужно будет скорректировать выделение атомов, чтобы выделить правильное количество элементов. В настоящее время вы также выделяете только один элемент памяти размером sizeof(struct Atom). Массив из таких элементов требует умножения размера одного элемента на количество элементов.

Функция calloc() хороша тем, что она выделяет массив за вас и инициализирует содержимое всех элементов нулем. malloc() ничего не делает для инициализации возвращаемой памяти и может привести к распространению непредсказуемых значений в вашей программе. Если вы используете malloc(), а не calloc(), вы должны позаботиться об инициализации элементов массива. Даже при использовании calloc() необходимо инициализировать все ненулевые элементы.


Обратите внимание, что я убрал приведение из возвращаемого значения malloc. Если вы пишете код на C, то и компилировать его следует как C-код. Компилятор не будет жаловаться на отсутствие приведения из void *, если только вы не компилируете в режиме C++. Исходные файлы на языке Си должны заканчиваться расширениями .c, а не .cpp.


Как заметил Вальтер Мундт, вы случайно вызываете free() на члене одного из ваших входных параметров, который вы присвоили указателю atoms. Вам придется исправить это самостоятельно; вышеприведенная freeptr() не выделит для вас эту ошибку.


Другие писали, что вы не можете использовать printf() для надежного определения места сбоя вашей программы. Вывод printf() буферизируется, и его появление задерживается.

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

Не имея такой возможности, замените:

printf("Program ran to point A.\n");

на:

fprintf(stderr, "Program ran to point A.\nPress return.\n");
fflush(stderr); /* Force the output */
fflush(stdin);  /* Discard previously-typed keyboard input */
fgetc(stdin);   /* Await new input */
fflush(stdin);  /* Discard unprocessed input */

В целом, я предлагаю вам пока не использовать язык C. Компьютеры в наши дни настолько быстры, что я бы задался вопросом, почему вы вообще рассматривали язык C.

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

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

1
ответ дан 8 December 2019 в 02:16
поделиться

Ошибка сегментации при возврате обычно свидетельствует о поврежденном стеке.

10
ответ дан 8 December 2019 в 02:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: