簡體   English   中英

C中簡單可變參數字符串連接的內存分配

[英]Memory allocation for simple variadic string concatenation in C

我有以下測試函數可復制並連接可變數量的字符串參數,並自動分配:

char *copycat(char *first, ...) {
    va_list vl;
    va_start(vl, first);
    char *result = (char *) malloc(strlen(first) + 1);
    char *next;
    strcpy(result, first);
    while (next = va_arg(vl, char *)) {
        result = (char *) realloc(result, strlen(result) + strlen(next) + 1);
        strcat(result, next);
    }
    return result;
}

問題是,如果我這樣做:

puts(copycat("herp", "derp", "hurr", "durr"));

它應該打印出一個16字節的字符串"herpderphurrdurr" 而是打印出一個42字節的字符串,它是正確的16字節加上26個字節的垃圾字符。

我不太確定為什么。 有任何想法嗎?

variable-argument-list函數無法神奇地知道有多少個參數,因此,您很可能會遍歷堆棧,直到碰巧碰到NULL為止。

您要么需要一個numStrings參數,要么在字符串列表之后提供一個明確的null終止符參數。

您的清單上需要一個標記標記:

puts(copycat("herp", "derp", "hurr", "durr", NULL));

否則, va_arg實際上不知道何時停止。 由於您正在調用未定義的行為,因此您將成為垃圾的事實純屬偶然。 例如,當我按原樣運行您的代碼時,我遇到了段錯誤。

可變參數函數(例如printf需要某種指示來指示要傳入多少個項目: printf本身使用前面的格式字符串來弄清楚這一點。

兩種通用方法是計數(或格式字符串),當您不能將可能的值之一用作標記(末尾有一個標記)時,此方法很有用。

如果您可以使用前哨(例如,對於指針,則為NULL;對於非負有符號整數,則為-1),通常情況下更好,因此您不必對元素進行計數(並且可以獲取元素計數和元素列表不對)。


請記住, puts(copycat("herp", "derp", "hurr", "durr")); 這是內存泄漏,因為要分配內存然后丟失指向它的指針。 使用方法:

char *s = copycat("herp", "derp", "hurr", "durr");
puts(s);
free (s);

是解決此問題的一種方法,如果分配失敗,您可能需要放入錯誤檢查代碼。

我從您的代碼中了解到,您假設一旦“彈出”每個參數,va_next將返回NULL。 這是錯誤的,因為va_next絕對無法確定參數的數量:您的while循環將一直運行,直到隨機命中NULL。

解決方案:提供參數數量,或添加帶有附加“ NULL”參數的函數調用。

PS:如果您想知道為什么printf不需要這樣的附加參數,那是因為期望的參數數量是從格式字符串(“%flag”的數量)推導出來的

作為其他答案的補充,將NULL用作可變參數的參數時,應將NULL轉換為期望的類型: (char *)NULL 如果將NULL定義為0,則將存儲一個int,當int的大小為sime且指針由所有位0表示NULL時,該int會意外地工作。但這不能保證,因此您可能會遇到奇怪的情況移植代碼或什至僅更改編譯器時很難調試的行為。

正如其他人提到的, va_arg不知道何時停止。 調用函數時,您可以提供NULL (或其他標記)。 只是一些注意事項:

  • 您必須在從mallocrealloc獲得的指針上調用free
  • 沒有理由在C中realloc mallocrealloc的結果。
  • 調用realloc ,最好將返回值存儲到一個臨時變量中。 如果realloc無法重新分配足夠的內存,它將返回NULL但原始指針不會釋放。 如果您使用realloc你的方式,它是無法重新分配內存,那么你已經失去了原有的指針和你的后續調用strcat很可能會失敗。 您可以這樣使用它:

     char *tmp = realloc(result, strlen(result) + strlen(next) + 1); if (tmp == NULL) { // handle error here and free the memory free(result); } else { // reallocation was successful, re-assign the original pointer result = tmp; } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM