Ошибка сегментации C до / во время оператора возврата - PullRequest
6 голосов
/ 15 июня 2010

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

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, а затем обнаруживает ошибки где-то перед тем, как добраться до того места, где яназывается функция, которую я делаю как:

hPos = findHydrogen((&aminoAcid[i]), nPos, diff, totRead);
printf("%d\n", hPos);

Ответы [ 10 ]

10 голосов
/ 15 июня 2010

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

8 голосов
/ 15 июня 2010

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

Эти строки выглядят странно, хотя:Вы отбрасываете указатель, возвращенный malloc.То же самое с bonds, и то же самое снова внутри вашего цикла for.

5 голосов
/ 15 июня 2010

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

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

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

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

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 и скопировать атомы по отдельности вместе с их содержимым, которое затем вам нужно будет индивидуально free в точке возврата.

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

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

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

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

4 голосов
/ 15 июня 2010

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

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[], чтовыдаёт ошибку сегментации.

4 голосов
/ 15 июня 2010

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

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

Во-вторых, вы не ограничиваетесь проверкой выражения bonds[i] - totRead.

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

3 голосов
/ 15 июня 2010

РЕДАКТИРОВАТЬ: прочитайте это Доступ к значениям массива с помощью арифметики указателей и подписки в 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 голосов
/ 15 июня 2010

Это странно:

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

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

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

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

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

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

3 голосов
/ 15 июня 2010

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

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

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

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

1 голос
/ 15 июня 2010

У вас есть #include <stdio.h> в файле?Интересно, вы получаете вызов на printf(), который использует неявное объявление printf() и, следовательно, может использовать неправильное соглашение о вызовах.

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

1 голос
/ 15 июня 2010

Где вы написали:

/* 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 нет встроенного оператора равенства (=) для копирования массивов, как вы, похоже, и предполагали.Вместо этого вы теряете указатель на выделенную память, ранее сохраненную в переменной atoms, и затем начинаете первую итерацию цикла с atoms, указывающим на «входную копию» массива атомов.


Часть проблемы заключается в том, что вы вызываете 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Ошибки и Сегментация Нарушения сразу после вас 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);

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

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

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

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


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

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

...