2016-08-30 55 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