返回语句之前/期间的C分段错误
我在返回语句之前打印我正在返回的值,并告诉我的代码打印函数调用后立即返回的值。 但是,在我的第一个print语句之后和第二个print语句之前,我得到了一个分段错误(也有趣的是,这总是在第三次调用函数时发生;从不在第一次或第二次,从不第四次或以后)。 我尝试打印出我正在处理的所有数据,看看我的其余代码是否正在做某些事情,但也许不应该这样做,但到目前为止,我的数据看起来还不错。 这是function:
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);
从这段代码猜测错误的位置并不容易(这里几乎每行代码都有可能存在错误) – 可能你的某个地方有缓冲区溢出,但是如果你在* nix上运行你的程序valgrind ,你应该能够很快找到错误。
这些线看起来很奇怪:
atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); atoms = (*amino).atoms;
当你丢弃malloc返回的指针时,你正在泄漏内存。 bonds
,在你的for循环中再次发生同样的事情。
返回时的分段错误通常表示存在损坏的堆栈。
编辑嗯,你左右泄漏记忆,但不像我想的那样。 固定顺序如下:
具体来说,当你这样做时:
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)中分配一些内存并将地址放在atoms
中。 然后,你扔掉那个地址,然后将atoms
指向(2)中amino
结构内部的一部分。 然后你用一个primefaces分配第二个指针。 最后,你可以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
循环并单独复制primefaces及其内容,然后您必须在返回点单独free
。
…或者如果您只想从结构中读取primefaces数据,可能就是这样:
atoms = (*amino).atoms; // delete 1, 3, 4 entirely and just read directly from the structure
其他答案谈论diff
和其他问题的空间量可能也值得研究。
编辑:修复调用序列以匹配代码示例。
这里有很多错误。
我注意到的第一件事是你正在泄漏内存(你在(struct Atom *) malloc(sizeof(struct Atom) * numAtoms)
分配一些内存,然后用氨基结构中的指针覆盖指针); 你用(int *) malloc(sizeof(int) * numBonds);
做同样的事情(int *) malloc(sizeof(int) * numBonds);
。
其次,你没有限制 – 检查表达bonds[i] - totRead
。
第三,我认为这是你崩溃的地方,你在这里覆盖你的primefaces指针: atoms = (struct Atom *) malloc(sizeof(struct Atom)); free(atoms);
atoms = (struct Atom *) malloc(sizeof(struct Atom)); free(atoms);
这会使primefaces指向无效的记忆。
这里是代码部分的一小部分重写,用于演示内存泄漏:
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[]
界限,这可能是段错误。
听起来你正在使用print语句调试分段错误:C中的一个大禁忌。
问题是stdout在大多数系统上都是缓冲的,这意味着分段错误的实际发生时间比你想象的要晚。 使用printf无法可靠地确定程序何时是segfaulting。
相反,您应该使用像gdb这样的调试器,它将告诉您导致分段错误的确切代码行。
如果你不知道如何使用gdb,这是我在Google上找到的一个快速教程: http : //www.cs.cmu.edu/~gilpin/tutorial/
这很奇怪:
bonds = (int *) malloc(sizeof(int)); free(bonds); atoms = (struct Atom *) malloc(sizeof(struct Atom)); free(atoms);
你分配内存,然后你立即释放它,让你的指针指向未分配的内存。
这条线看起来也很危险:
atoms[bonds[i] - totRead].type[0] == 'H'
确保使用索引保留在数组中。
编辑:去读这个通过指针算法访问数组值与C中的下标它应该有助于你理解什么是指针和数组
这些行实际上对代码的作用没有任何实际的净影响,因此您可以删除它们
bonds = (int *) malloc(sizeof(int)); free(bonds); atoms = (struct Atom *) malloc(sizeof(struct Atom)); free(atoms);
这里的malloc线是无用的并且导致泄漏,因为你在做完之后将指针从氨基结构分配给primefaces和键。
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; } }
你在文件中有#include
吗? 我想知道你是否正在调用printf()
,它使用了printf()
的隐式声明,因此可能使用了错误的调用约定。
您正在使用的编译器/平台是什么? 你从构建中得到任何警告吗?
你写的地方:
/* 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
指向primefaces数组的“输入副本”。
部分问题是你在内存上调用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()
之前。
重新编译代码后,在free(atom)
后会立即发现Bus Errors和Segmentation Violation错误。 这是正确的, freeptr()
的目的是引导你的代码立即崩溃,而不是你的代码滥用指针并导致你很难调试的问题的当前情况。
要最终纠正你的内存分配,一定要转换行:
bonds = (int *) malloc(sizeof(int)); free(bonds);
应该是:
free(bonds); bonds = (int *) malloc(sizeof(int));
您使用参数diff
就像传入至少三(3)个元素的数组一样。 您应该validation调用者是否传递了足够的内存。
分配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
的分配以分配正确数量的元素。 目前,您还只分配一个size sizeof(struct Atom)
内存元素。 这些元素的数组要求您将一个元素的大小乘以元素的数量。
calloc()
函数很好,因为它为你分配一个数组,并将所有元素的内容初始化为零。 malloc()
不会初始化返回的内存,并且可能导致在程序中传播不可预测的值。 如果使用malloc()
而不是calloc()
,则必须注意初始化数组元素。 即使使用calloc()
,也必须初始化任何非零元素。
请注意,我从malloc
的返回值中删除了malloc
。 如果您正在编写C代码,则应将其编译为C代码。 除非您在C ++模式下编译,否则编译器不会抱怨缺少来自void *
的强制转换。 C源文件应以.c
文件扩展名结尾,而不是.cpp
。
正如Walter Mundt所指出的那样,你不小心在你的一个输入参数的成员上调用了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语言。 但是C不适合一切。 C非常适合操作系统,嵌入式系统,高性能计算以及其他成功的主要障碍是缺乏对计算机器的低级访问的情况。
在您的情况下,您似乎是一名科学家或工程师。 我建议你学习和使用Python。 Python可以轻松阅读,轻松validation的程序,您可以与您的化学家或工程师分享。 C不适合像Python那样快速编写健壮的代码。 在不太可能的未来事件中,Python的速度不够快,您还可以使用其他解决方案。