2013-04-03 69 views
1

我還沒有再次關於C的(ANSI-C通過VS2012編譯)的運作C:動態字符陣列崩潰堆

我重構一個獨立的程序(.EXE)到一個.dll問題。到目前爲止,這工作得很好,但當涉及到日誌記錄時,我偶然發現了問題。讓我解釋一下:

原始程序 - 運行時 - 寫了一個日誌文件並將信息打印到屏幕上。由於我的DLL是要在一個Web服務器上運行,被許多人同時訪問有

  • 沒有真正的機會,妥善處理日誌文件(及後他們清理)
  • 沒有控制檯窗口任何人看見

因此,我的目標是將所有可以放在日誌文件或屏幕上的所有內容寫入字符串變量(我知道C中沒有字符串),然後我可以通過請求給調用者(也是一個DLL,但用C#編寫)。

由於用C這樣的事情是不可能的:

char z88rlog; 
z88rlog="First log-entry\n"; 
z88rlog+="Second log-entry\n"; 

我有兩個可能性:

  1. char z88rlog[REALLY_HUGE];
  2. 動態分配內存

在我心中的第一方式是被忽略,因爲:

  • 記憶的潛在浪費是相當巨大的
  • 我可能仍然需要更多的內存比REALLY_HUGE,從而創造一個緩衝區溢出

這給我留下了第二種方式。我已經做了一些工作,並提出了兩種解決方案,其中任何一種都無法正常工作。

/* Solution 1 */  
void logpr(char* tmpstr) 
{ 
    extern char *z88rlog; 
    if (z88rlog==NULL) 
    { 
     z88rlog=malloc(strlen(tmpstr)+1); 
     strcpy(z88rlog,tmpstr); 
    } 
    else 
    { 
     z88rlog=realloc(z88rlog,strlen(z88rlog)+strlen(tmpstr)); 
     z88rlog=strcat(z88rlog,tmpstr); 
    } 
} 

在溶液1(相當於解決方案2,你會發現)我穿過char tmpstr[255];我的新的日誌條目。我的「日誌文件」z88rlog是全局聲明的,所以我需要extern來訪問它。然後我檢查內存是否已分配給z88rlog。如果沒有,我將記錄分配給我的日誌條目的大小(我的\0爲+1),並將tmpstr的內容複製到z88rlog。如果是,我重新分配內存z88rlog它的大小+的長度爲tmpstr(+1)。然後使用strcat加入兩個「字符串」。使用斷點的直接窗口我obtainded以下的輸出:

z88rlog 
0x00000000 <Schlechtes Ptr> 
z88rlog 
0x0059ef80 "start Z88R version 14OS" 
z88rlog 
0x0059ef80 "start Z88R version 14OS 
opening file Z88.DYNÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þîþîþîþ" 

它顯示logpr連續三個電話(斷點前strcpy/strcat)。最後的難以區分的亂碼是由內存分配引起的。之後,VS發出一條錯誤消息,指出調試器在realloc.c中設置了一個斷點。因爲這顯然是行不通的我炮製我的妙解2:

/* Solution 2 */ 
void logpr(char* tmpstr) 
{ 
    extern char *z88rlog; 
    char *z88rlogtmp; 
    if (z88rlog==NULL) 
    { 
     z88rlog=malloc(strlen(tmpstr)+1); 
     strcpy(z88rlog,tmpstr); 
    } 
    else 
    { 
     z88rlogtmp=malloc(strlen(z88rlog)+strlen(tmpstr+1)); 
     z88rlogtmp=strcat(z88rlog,tmpstr); 
     free(z88rlog); 
     z88rlog=malloc(strlen(z88rlogtmp)+1); 
     memcpy(z88rlog,z88rlogtmp,strlen(z88rlogtmp)+1); 
     free(z88rlogtmp); 
    } 
} 

這裏我的目標是創造我的日誌文件的副本,釋放原件的記憶在新的大小創造的原新內存並將內容複製回來。並且不要忘記釋放臨時副本,因爲它是通過malloc分配的。當它達到free時,它會立即崩潰,並再次告訴我堆可能被破壞。

因此,讓我們暫時免費評論。這種方法的效果更好 - 對我來說非常舒服 - 但是在建立日誌字符串時,並不是所有來自z88rlogtmp的字符都被複制。但一切仍然正常。直到突然間我又告訴堆可能會被打破,調試器把一個斷點在_heap_alloc (size_t size)in malloc.csize末有 - 根據調試器 - 的1041

所以此值我有2個(或3)的方式我想實現這種「字符串增長」,但沒有任何作用。給我的大小的錯誤可能會導致陣列變大的結論?我希望我解釋清楚我想做什麼,有人可以幫助我:-)提前致謝!

諷刺也許我應該去買一些新的電腦堆。它適合RAM插槽嗎?任何人都可以推薦一個好品牌嗎? 反諷

回答

1

你似乎有很多問題。要開始在您的realloc呼叫您不分配空間終止'\0'字符。在你的第二個解決方案中,你有strlen(tmpstr+1)這是不正確的。在第二種解決方案中,您還使用strcat來追加到現有緩衝區z88rlog,如果它不夠大,則會覆蓋未分配的內存,或者覆蓋分配給其他內容的數據。 strcat的第一個參數是目標,這也是該函數返回的內容,因此您也會釋放新分配的內存。

第一個解決方案,與realloc,應該工作正常,如果你只是記得分配額外的字符。

+0

太棒了!修復我的'realloc'的確有竅門!修復'strlen(tmpstr + 1)'然而沒有。這兩種解決方案如此不同? – lhiapgpeonk

+0

@lhiapgpeonk是的,這兩種解決方案是不一樣的,因爲我已經在我的更新的答案中指出。 –

2

這是一個錯誤的解決方案1 ​​

z88rlog=realloc(z88rlog,strlen(z88rlog)+strlen(tmpstr)); 

,因爲沒有空間分配給終止空字符。請注意,您必須將realloc()的結果存儲到臨時變量中,以避免在發生故障時發生內存泄漏。要糾正:

char* tmp = realloc(z88rlog, strlen(z88rlog) + strlen(tmpstr) + 1); 
if (tmp) 
{ 
    z88rlog = tmp; 
    /* ... */ 
} 

誤區溶液2

z88rlogtmp=malloc(strlen(z88rlog)+strlen(tmpstr+1)); 
             /*^^^^^^^^^*/ 

它calulating比tmpstr長度少一個。要糾正:導致不確定的行爲

z88rlogtmp=malloc(strlen(z88rlog) + strlen(tmpstr) + 1); 

指針重新分配:

z88rlogtmp=strcat(z88rlog,tmpstr); 
    /* Now, 'z88rlogtmp' and 'z88rlog' point to the same memory. */ 

    free(z88rlog); 
    /* 'z88rlogtmp' now points to deallocated memory. */ 

    z88rlog=malloc(strlen(z88rlogtmp)+1); 
    /* This call ^^^^^^^^^^^^^^^^^^ is undefined behaviour, 
     and from this point on anything can happen. */ 

    memcpy(z88rlog,z88rlogtmp,strlen(z88rlogtmp)+1); 
    free(z88rlogtmp); 

此外,如果代碼是一個網絡服務器,它在多線程環境幾乎肯定工作中執行。由於你有一個全局變量,它將需要同步訪問。

+0

謝謝你的回答!同步訪問意味着什麼?我的變量是在我的C-DLL中全局定義的。 – lhiapgpeonk

+0

@lhiapgpeonk,如果多個線程試圖修改'z88rlog',那麼你有競爭條件。訪問'z88rlog'必須被sycnronized,這意味着只有線程可以訪問修改它在任何時間。 – hmjd

+0

這聽起來像我可以P /從任何地方調用我的dll,我的dll只保存一組變量。這太可怕了!如果我只能從C#中的dll創建一種對象。你能指出我對如何實現這種同步的一些解釋嗎? – lhiapgpeonk

1

在解決方案1中,您需要分配空間來終止NULL字符。因此,realloc的應該包括一個更大的空間,即

z88rlog=realloc(z88rlog,strlen(z88rlog)+strlen(tmpstr) + 1); 

在第二個解決方案,我不知道這z88rlogtmp=strcat(z88rlog,tmpstr);因爲z88rlog是目標字符串。如果您只希望執行malloc,那麼

z88rlogtmp=malloc(strlen(z88rlog)+1 // Allocate a temporary string 
strcpy(z88rlogtmp,z88rlog); // Make a copy 
free(z88rlog); // Free current string 
z88rlog=malloc(strlen(z88rlogtmp)+ strlen(tmpstr) + 1)); //Re-allocate memory 
strcpy(z88rlog, z88rlogtmp); // Copy first string 
strcat(z88rlog, tmpStr); // Concatenate the next string 
free(z88rlogtmp); // Free the Temporary string