2016-08-30 54 views
0

我是一个学习c的初学者,并且分割对我来说会发生很多次。我也在网上做了一些关于分段错误的研究:一些原因是分配内存问题,空指针或内存访问问题。但我很困惑,为什么有时代码可以工作,但有时它说分段错误?下面是代码,我在两个insertAtdestroyList功能得到这个问题:修复分割错误有时在C

#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

你有一个很奇怪的分裂全局变量和局部变量... –

+0

尝试使用gdb进行调试 – Dave

+0

全局变量'counter'不会为* next *列表插入重置。将它作为'insertAt'中的局部变量,并在函数的开始处对其进行初始化。除非完全有必要,否则请勿使用全局变量。 –

回答

0

在gdb下运行程序,就像这样:

$ gdb ./a.out 
(gdb) run 
... 
Segmentation fault 
(gdb) bt 

,将打印回溯,显示现货你的代码在哪里引起错误以及任何调用它的函数。如果段错误发生在库函数中,那么继续往下看直到你的代码,看看你能在那里修复哪些东西。

+3

这是很好的建议,但不是真正的问题答案。 –

+0

是的,这是一个答案。这是一个工具,您可以使用它找到您的程序为什么会出现故障的答案。 – Chad

+2

这不是对提出的问题的回答*,因为我将其解释为“为什么此程序有时会出现段错误?”。你的建议会比答案更好。 –

1

我也在网上做了一些关于分段错误的研究:一些原因是分配内存问题,空指针或内存访问问题。

分割故障几乎总是表示你的程序试图访问不属于它的内存,或者在某种程度上,它是不允许访问它。你可以把它分解成不同的方式,程序可能会做,但总的原则是要确保你解引用唯一有效的指针,而你试图修改只是修改的数据。

但我很困惑,为什么有时代码工作,但有时它说分段错误?

C不指定任何特定的行为产生段故障。该标准甚至没有包含术语“分段错误”。它,然而,说起“未定义行为” - 这就是,如果你执行代码不被C的语义规则和标准库遵守你会得到什么。

分割故障是在很多系统中未定义行为的一个可能的表现,但是这是外界C'S范围。 C不承诺在任何特定情况下的任何特定形式的未定义行为 - 它不能,因为这种行为将被定义,而不是未定义。因此,可以看到的其他形式的未定义行为之一就是程序员所期望的任何行为。事实上有时候会这样。

此外,也可以是,一个给定的程序有未定义的行为的情况下 - 这可能在段故障体现 - 仅在某些条件下,诸如用于特定输入。

在任何情况下,您的程序有时甚至始终(尽可能确定)的行为如预期一样,并不证明它没有未定义的行为。


至于你特定的代码,它有一些缺陷可能导致它有时展现未定义的行为。其中有:

  • 您使用的malloc()返回值没有检查它是否为null。通过返回NULL malloc()信号内存分配失败,如果以后尝试取消引用该则调用未定义的行为。

  • 当它插入初始节点到列表中,并且当它在列表的末尾添加一个节点,insertAt()失败来设置新的节点的指针next,留下与一个不确定的值。当任何函数稍后遍历列表时,它会评估该不确定值,从而产生未定义的行为。在实践中,如果不确定值结果为空指针值,这可能会发生预期的行为。这绝不是保证,但它也不是完全不可能的。

  • main()函数试图打印的清单已在destroyList()被释放后,由当时的无效myList指针传递给printList()

1

你想要的工具是Valgrind。它会为你找到各种隐藏的内存问题。

例如,此代码“有效”。

$ 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) 

这是一条错误消息,之后的函数栈调用导致它,那么可能造成的任何相关的错误。

无效的写入大小1”告诉我,我走了一个字节的分配内存。 by 0x100000F2C: main (test.c:9)说它发生在test.c的第9行,它是strcpy,并且呼叫链上面的行确认(它说stpcpy,因为strcpy可能只是一个围绕stpcpy的宏)。

地址0x100a8f6d3在大小为3的块被分配后的0字节”告诉我错误可能是错误的内存分配的结果。 by 0x100000F11: main (test.c:7)表示它在test.c第7行,这是calloc呼叫,由堆栈中的下一个呼叫at 0x10000A1B9: calloc (vg_replace_malloc.c:715)确认。

这一切都说我分配了比我需要的少一个字节。由于C字符串的尾部为空字符,因此您需要4个字节作为3字节的字符串。

(附注:不要使用strcpy

但我感到困惑的是,为什么有时代码的工作,但有时它说段错误?

这是因为C允许你涂写任何你想要的内存。现代操作系统至少让你在自己的进程内存中。如果你重写了一些重要的东西,或者如果你尝试阅读或写入记忆中,你就不应该这样做,那么这可能会导致各种各样的问题,或者你可能会很幸运!由于内存每次分配的方式稍有不同,因此每次运行带有内存问题的程序都可能会有所不同。

我上面的示例代码工作,即使它写入无效的内存。这只是一个字节,所以也许它很幸运,写在一个没有人关心的地方。或者,也许calloc分配比需要更多的内存。

要点是,学习valgrind或其他内存检查器,运行它,并修复它说的任何东西。

(PS为valgrind OS安装包,它会被调整了您的操作系统的漏洞和怪癖,否则你可能会得到各种各样的警告有关操作系统的自己的代码。)

+0

Valgrind是一个合适的工具,是的。但是,请注意,功能实现在问题中提供 - 您只需要在iframe中向下滚动即可。 –

+0

@JohnBollinger D'oh! – Schwern