2017-03-05 90 views
2

免責聲明,這是一個學校作業的幫助。這就是說,我的問題只發生在大約50%的時間。這意味着如果我編譯並運行我的代碼而不進行編輯,有時它會將其貫穿到最後,而其他時間則不會。通過使用多個打印語句,我確切地知道問題發生的位置。這個問題發生在我第二次打電話給hugeDestroyer(正好在打印354913546879519843519843548943513179部分之後),更確切地說是在免費(p->數字)部分。Calloc/Malloc並釋放空間太大或太大?

我試過了這裏找到的建議(free a pointer to dynamic array in c),並將指針設置爲NULL後釋放它們沒有運氣。

通過一些挖掘和靈魂搜索,我已經瞭解了一些關於(How do malloc() and free() work?)的免費作品,我想知道我的問題是來自用戶Juergen在他的回答中提到的,並且我正在「覆蓋」管理數據免費清單。

要清楚,我的問題是雙重的。

是免費的(p->數字)在語法上是正確的,如果是這樣的話,爲什麼我在運行代碼時會遇到一半的麻煩?其次,我如何防範這種行爲在我的職能?

#include <stdio.h> 
#include <stdlib.h> 
#include <limits.h> 


typedef struct HugeInteger 
{ 
    // a dynamically allocated array to hold the digits of a huge integer 
    int *digits; 

    // the number of digits in the huge integer (approx. equal to array length) 
    int length; 
} HugeInteger; 


// Functional Prototypes 

int str2int(char str) //converts single digit numbers contained in strings to their int value 
{ 
    return str - 48; 
} 

HugeInteger *parseInt(unsigned int n) 
{ 
    int i = 0, j = 0; 
    int *a = (int *)calloc(10, sizeof(int)); 
    HugeInteger *p = (HugeInteger *)calloc(1, sizeof(HugeInteger)); 

    if(n == 0) 
    { 
     p->digits = (int *)calloc(1, sizeof(int)); 
     p->length = 1; 
     return p; 
    } 

    while(n != 0) 
    { 
     a[i] = n % 10; 
     n = n/10; 
     i++; 
    } 

    p->length = i; 
    p->digits = (int *)calloc(p->length, sizeof(int)); 

    for(i = 0; i <= p->length; i++, j++) 
     p->digits[j] = a[i]; 

    return p; 
} 

HugeInteger *parseString(char *str) //notice datatype is char (as in char array), so a simple for loop should convert to huge int array 
{ 
    int i = 0, j = 0; 
    HugeInteger *p = (HugeInteger *)calloc(1, sizeof(HugeInteger)); 

    if(str == NULL) 
     { 
     free(p); 
     p = NULL; 
     return p; 
     } 

    else 
    { 
     for(i=0; str[i] != '\0'; i++) 
      ; 
     p->length = i; 
     p->digits = (int *)calloc(p->length, sizeof(int)); 

     for(; i >= 0; i--) 
      p->digits[j++] = str2int(str[i - 1]); 
    } 

    return p; 
} //end of HugeInteger *parseString(char *str) 


HugeInteger *hugeDestroyer(HugeInteger *p) 
{ 
//printf("No problem as we enter the function\n"); 
    if(p == NULL) 
     return p; 
//printf("No problem after checking for p = NULL\n"); 
    if(p->digits == NULL) 
    { 
     free(p); 
     p = NULL; 
     return p; 
    } 
//printf("No Problem after checking if p->digits = NULL\n"); 
    //else 
    //{ 
     free(p->digits); 
printf("We made it through free(p->digits)\n"); 
     p->digits = NULL; 
printf("We made it through p->digits = NULL\n"); 
     free(p); 
printf("We made it through free(p)\n"); 
     p = NULL; 
printf("We made it through p = NULL\n"); 
     return p; 
    //} 

    //return NULL; 
}//end of HugeInteger *hugeDestroyer(HugeInteger *p) 

// print a HugeInteger (followed by a newline character) 
void hugePrint(HugeInteger *p) 
{ 
    int i; 

    if (p == NULL || p->digits == NULL) 
    { 
     printf("(null pointer)\n"); 
     return; 
    } 

    for (i = p->length - 1; i >= 0; i--) 
     printf("%d", p->digits[i]); 
    printf("\n"); 
} 

int main(void) 
{ 
    HugeInteger *p; 

    hugePrint(p = parseString("12345")); 
    hugeDestroyer(p); 

    hugePrint(p = parseString("354913546879519843519843548943513179")); 
    hugeDestroyer(p); 

    hugePrint(p = parseString(NULL)); 
    hugeDestroyer(p); 

    hugePrint(p = parseInt(246810)); 
    hugeDestroyer(p); 

    hugePrint(p = parseInt(0)); 
    hugeDestroyer(p); 

    hugePrint(p = parseInt(INT_MAX)); 
    hugeDestroyer(p); 

    //hugePrint(p = parseInt(UINT_MAX)); 
    //hugeDestroyer(p); 

    return 0; 
} 
+1

您是否嘗試過使用調試器? –

+1

'for(; i> = 0; i - )'循環比你分配的空間多寫入一個數字(例如,如果'p-> digits == 1'那麼你爲1個int分配空間,但是這個循環運行兩次,'i == 1',然後'i == 0',第二次迭代寫入'p-> digits [1]',這是超出範圍的) –

+0

我已經使用了帶有某些效果的codeBlocks的調試器,是什麼讓我到免費(p->數字)部分,但它只是有一個內存地址作爲註釋指向該行,並沒有真正有用的除了顯示我。 我確實改變了你指出的片段(; i - 1> = 0; i--),現在我可以在segfaulting之前將它放到INT_MAX部分,所以這絕對是進步。似乎我的問題可能不是免費的,但超越了界限。 –

回答

2

首先,真的很突出的問題。你對話題做了大量的研究,一般來說,你自己解決了這個問題,我在這裏主要是爲了證實你的發現。

是否免費(p-> digits)語法上正確,如果是這樣,爲什麼我在運行代碼時會遇到一半的麻煩?

語法正確。 @Shihab在評論中建議只發布p->digits和版本p,但這樣的建議是錯誤的,它會導致內存泄漏。有一條簡單的規則:對於每個calloc,您最終必須免費撥打電話,因此您目前的解決方案是釋放p->digits,然後p完全正常。

但是,程序在有效行上失敗。這怎麼可能?快速回答:由於負責跟蹤已分配/空閒塊列表的元信息的損壞,免費無法完成其工作。在某些程序上,程序損壞了元信息,但這只是在試圖使用它時才顯示出來。

正如您已經發現的那樣,在大多數實現中,內存例程(如calloc)會導致帶有前置元信息的緩衝區分配。您會收到指向緩衝區的指針,但在該指針對進一步緩衝區管理(例如釋放)至關重要之前,還會收到一小段信息。將11個整數寫入預期爲10的緩衝區中,可能會破壞緩衝區後面塊的元信息。腐敗究竟是否真的發生了,其後果是什麼,嚴重依賴於實現細節和當前的內存對齊(緩衝區後面是什麼塊,究竟是什麼元數據被破壞)。我並不感到驚訝,你會看到每兩次執行一次崩潰,在我的系統上觀察到100%的崩潰再現,這也不意外。

其次,我該如何防範這種行爲在我的職能?

讓我們從修復溢出開始。有他們夫婦:

  • parseString:循環for(; i >= 0; i--)執行length+1倍,因此p->digits是飛越
  • parseInt:循環for (i = 0; i <= p->length; i++, j++)執行length+1倍,因此p->digits被溢出到

直接訪問使用C++進行內存管理很容易出錯並且很難調試。內存泄漏和緩衝區溢出是程序員生活中最糟糕的噩夢,除非您正在學習如何應對它,否則通常會更好地簡化/減少動態內存的直接使用。如果你需要堅持大量的直接內存管理,看看valgrind,它的目的是檢測所有這些東西。

順便說一句,您的程序中還有一個內存泄漏:每個撥打parseInt的電話都會爲a分配緩衝區,但永遠不會釋放它。