我是学习c的初学者,我的分词多次发生。我还在网上做了一些关于分段错误的研究:一些原因是分配内存问题,空指针或内存访问问题。但我很困惑,为什么有时代码工作,但有时它说分段错误?以下是我在insertAt
和destroyList
函数中遇到此问题的代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct NODE{
int data;
struct NODE* next;
} node;
node* insertAt(node*, int, int);
void printList(node*);
void destroyList(node*);
node* myList;
int counter = -1;
int main()
{
myList = NULL;
int pos, input;
myList = insertAt(myList, 0, 333);
myList = insertAt(myList, 0, 555);
myList = insertAt(myList, 1, 222);
myList = insertAt(myList, 3, 444);
printf("My List:\n");
printList(myList);
destroyList(myList);
printf("After Destroy:\n");
printList(myList);
return 0;
}
node* insertAt(node* head, int pos, int newData)
{
node* temp = (node*) malloc(sizeof(node));
temp->data = newData;
counter++;
if(head == NULL){
head = temp;
return head;
}else if (pos == 0)
{
temp->next = head;
head = temp;
return head;
}else if(head != NULL && pos > counter){
node* current = head;
node* temp2 = current;
while(current != NULL){
temp2 = current;
current = current->next;
}
temp->next = current;
temp2->next = temp;
return head;
}else
{
node* current = head;
while(pos-1>0){
current = current->next;
pos--;
}
temp->next = current->next;
current->next = temp;
return head;
}
}
void printList(node* head)
{
node* ptr = head;
while (ptr != NULL) {
printf("%i ", ptr->data);
ptr = ptr->next;
}
printf("\n");
}
void destroyList()
{
node* temp;
while(myList){
temp = myList;
myList = temp->next;
free(temp);
}
}
答案 0 :(得分:1)
我还在网上做了一些关于分段错误的研究:一些原因是分配内存问题,空指针或内存访问问题。
分段错误总是表示您的程序试图访问不属于它的内存,或者不允许访问它的方式。您可以将其分解为程序可能执行此操作的不同方式,但一般规则是确保仅取消引用有效指针,并尝试仅修改可修改的数据。
但我很困惑,为什么有时代码会工作,但有时会说分段错误?
C未指定任何特定行为产生分段错误。该标准甚至不包含术语&#34;分段错误&#34;。然而,它确实讨论了未定义的行为&#34; - 如果您执行的代码不符合C及其标准库的语义规则,那么这就是您所获得的。
分段错误是许多系统上未定义行为的一种可能表现形式,但这超出了C的范围。 C不承诺在任何特定情况下任何特定形式的未定义行为 - 它不能,因为那个行为将被定义,而不是未定义。由此可见,可以看到的其他形式的未定义行为是程序员想要的任何行为。实际上有时会看到这一点。
此外,可能的情况是,给定程序具有未定义的行为 - 这可能表现在分段故障中 - 仅在某些条件下,例如特定输入。
在任何情况下,您的程序有时甚至总是(尽可能确定)的行为与预期不符,并不能证明它没有未定义的行为。
至于你的特定代码,它有几个缺陷可能导致它有时表现出未定义的行为。其中包括:
使用malloc()
的返回值而不检查它是否为NULL。 malloc()
通过返回NULL来表示内存分配失败,如果稍后尝试取消引用,则调用未定义的行为。
当它将初始节点插入列表并在列表末尾添加节点时,insertAt()
无法设置新节点的next
指针,留下具有不确定价值的东西。当任何函数稍后遍历列表时,它会评估该不确定值,从而产生未定义的行为。实际上,如果不确定值被证明是空指针值,则可能会发生预期的行为。这绝不是保证,但也不是完全不可能的。
main()
函数通过将无效的destroyList()
指针传递给myList
来尝试在printList()
中取消分配后打印列表。< / p>
答案 1 :(得分:1)
您想要的工具是Valgrind。它会为你找到各种隐藏的记忆问题。
例如,此代码&#34;工作&#34;。
$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *string = calloc(3, sizeof(char));
strcpy(string, "foo");
printf("%s\n", string);
free(string);
return 0;
}
但是Valgrind发现了一个微妙的逐个内存错误。
$ make
cc -Wall -g test.c -o test
$ ./test
foo
$ valgrind ./test
==62034== Memcheck, a memory error detector
==62034== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==62034== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==62034== Command: ./test
==62034==
==62034== Invalid write of size 1
==62034== at 0x10043B5C0: _platform_memmove$VARIANT$Nehalem (in /usr/lib/system/libsystem_platform.dylib)
==62034== by 0x1001B8421: stpcpy (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x10022BBED: __strcpy_chk (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x100000F2C: main (test.c:9)
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
==62034== by 0x100000F11: main (test.c:7)
==62034==
==62034== Invalid read of size 1
==62034== at 0x10000B2C8: strlen (vg_replace_strmem.c:470)
==62034== by 0x1001EDA4B: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x1002166C0: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x1001EC381: vfprintf_l (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x1001EA21B: printf (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x100000F42: main (test.c:10)
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
==62034== by 0x100000F11: main (test.c:7)
==62034==
foo
==62034==
==62034== HEAP SUMMARY:
==62034== in use at exit: 26,553 bytes in 188 blocks
==62034== total heap usage: 273 allocs, 85 frees, 32,788 bytes allocated
==62034==
==62034== LEAK SUMMARY:
==62034== definitely lost: 0 bytes in 0 blocks
==62034== indirectly lost: 0 bytes in 0 blocks
==62034== possibly lost: 0 bytes in 0 blocks
==62034== still reachable: 0 bytes in 0 blocks
==62034== suppressed: 26,553 bytes in 188 blocks
==62034==
==62034== For counts of detected and suppressed errors, rerun with: -v
==62034== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 19 from 19)
摄取很多东西,但重要的是在test.c中,在它上面。看第一条消息......
==62034== Invalid write of size 1
==62034== at 0x10043B5C0: _platform_memmove$VARIANT$Nehalem (in /usr/lib/system/libsystem_platform.dylib)
==62034== by 0x1001B8421: stpcpy (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x10022BBED: __strcpy_chk (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x100000F2C: main (test.c:9)
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
==62034== by 0x100000F11: main (test.c:7)
这是一条错误消息,后面是导致它的函数调用堆栈,然后是可能导致它的任何相关错误。
&#34; 无效的写入大小1 &#34;告诉我,我已经从已分配的内存中走了一个字节。 by 0x100000F2C: main (test.c:9)
说它发生在test.c的第9行,strcpy
,调用链中的上面的行确认了(它说stpcpy
,因为strcpy
可能只是围绕stpcpy
)的宏。
&#34; 地址0x100a8f6d3在大小为3的块之后为0字节。&#34;告诉我错误可能是错误的内存分配的结果。 by 0x100000F11: main (test.c:7)
表示它位于test.c第7行,即calloc
来电,由堆叠中的下一个电话at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
确认。
这一切都说我分配的字节少于我需要的字节数。由于C字符串具有尾随空字符,因此3字节字符串需要4个字节。
(P.S.Don不使用strcpy
。)
但我很困惑,为什么有时代码会工作,但有时会说分段错误?
这是因为C允许你随意涂抹你想要的任何内存。现代操作系统至少让您在自己的进程内存中。如果你覆盖重要的东西,或者你试图读取或写入你不应该......或者你可以幸运的记忆,这可能会导致各种各样的问题!由于每次内存的分配略有不同,因此每次运行带有内存问题的程序可能会有不同的行为。
上面的示例代码即使写入无效内存也能正常工作。它只是一个字节,所以也许它很幸运,并写在没有人关心的地方。或者可能calloc
分配了比它需要的更多的内存。
点是,学习valgrind
或其他内存检查器,运行它,并修复它说的被破坏的任何内容。
(PS为valgrind
安装操作系统软件包。它将针对您的操作系统的错误和怪癖进行调整。否则您可能会收到有关操作系统自身代码的各种警告。)
答案 2 :(得分:0)
在gdb下运行你的程序,如下所示:
$ gdb ./a.out
(gdb) run
...
Segmentation fault
(gdb) bt
这将打印一个回溯,显示代码中导致错误的位置以及调用它的任何函数。如果在库函数中发生了段错误,请继续向下查看回溯直到找到代码,然后看看你能解决的问题。