在C

时间:2016-08-30 17:14:19

标签: c linked-list segmentation-fault

我是学习c的初学者,我的分词多次发生。我还在网上做了一些关于分段错误的研究:一些原因是分配内存问题,空指针或内存访问问题。但我很困惑,为什么有时代码工作,但有时它说分段错误?以下是我在insertAtdestroyList函数中遇到此问题的代码:

#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);
    }
}

3 个答案:

答案 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

这将打印一个回溯,显示代码中导致错误的位置以及调用它的任何函数。如果在库函数中发生了段错误,请继续向下查看回溯直到找到代码,然后看看你能解决的问题。