簡體   English   中英

動態分配和復制數組

[英]Dynamically allocating and copying an array

我有時會看到這樣的代碼:

char* copyStr(char* input) {
  int inputLength;
  char *answer;

  inputLength = strlen(input);

  answer = malloc(inputLength + 1);
  answer = input;

  return answer;
}

人們經常說這段代碼不起作用,而且這種模式

answer = malloc(inputLength + 1);
answer = input;

沒有意義。 為什么會這樣? 在我看來,代碼沒問題。 它為答案分配適量的 memory,然后將輸入復制到答案中。 例如,它似乎在我的測試中有效

int main()
{
   printf ("%s\n", copyStr("Hello world!"));
}

做我期望它做的事情。 那么它有什么問題呢?

簡而言之。 這段代碼:

var = foo();
var = bar();

在所有1種情況下都 100% 等價於:

foo();
var = bar();

此外,如果foo()沒有副作用,則它 100% 等效於最后一行:

// foo(); 
var = bar();

這適用於任何 function,包括malloc 如果我們暫時忘記malloc做了什么,只關注剛才所說的,我們可以很快意識到這段代碼的注釋中寫了什么:

answer = malloc(inputLength + 1);
// Here, the variable answer contains the return value from the call to malloc
answer = input;
// Here, it contains the value of input. The old value is overwritten, and
// is - unless you saved it in another variable - permanently lost.

malloc的作用非常簡單。 如果分配失敗,它將返回指向 memory 塊的指針或 NULL 指針。 2就是這樣。 您對ptr = malloc(size)之類的調用所做的事情絕對比將該地址存儲在指針變量ptr中更花哨。 並且指針變量與intfloat等其他變量一樣花哨。 一個int存儲一個 integer。 一個指針存儲一個 memory 地址。 這里沒有魔法。

1它是 100% 等價的,除了你正在做一些非常花哨的事情,比如用外部程序讀取變量var 2 malloc(0)可以返回一個非空指針,但實際上它並沒有什么區別,因為它是未定義的行為取消引用它,分配零字節是一個非常沒有意義的(哈哈,點)操作。

為了回答這個問題,讓我們先看一個稍微簡單一些的代碼片段。

int answer;

answer = 42;
answer = 0;

即使是最粗略的觀察者也會注意到第一個分配

answer = 42;

沒用。 它將42的值放入answer中,只是在下一瞬間被丟棄並替換為0 這樣那行代碼就可以完全扔掉了。

讓我們通過查看由 C 編譯器生成的優化匯編代碼來驗證這一點。 如我們所見,行answer = 42; 確實對生成的機器代碼沒有任何影響。

現在將其與有問題的代碼進行比較

answer = malloc(inputLength + 1);
answer = input;

如果在這種情況下類比推理是有效的,那么我們必須得出結論,第一個賦值是無用的,可以省略。 我們在answer中放置了一些東西( malloc的結果),但稍后會被丟棄並替換為其他東西。

當然,我們不能在沒有進一步研究的情況下說它是否適用,但是我們可以通過再次查看生成的程序集來證實我們的懷疑。 並且得到證實 編譯器甚至不會生成對mallocstrlen的任何調用。 它們確實沒用。


那么這種直覺在哪里

它為答案分配適量的 memory,然后將輸入復制到答案

分解?

問題在於指針和 arrays 之間的永恆混淆。

One may often see claims that in C, arrays are pointers, or that pointers are arrays, or that arrays and pointers are interchangeable, or any number of variations thereof. 這些說法都是虛假和誤導性的。 指針和 arrays 是完全不同的東西。 他們經常一起工作,但這遠非一體。 讓我們在代碼示例中分解指針和 arrays。

  • input是一個指針變量
  • input (大概)指向一個字符串,它是一個char數組
  • answer是另一個指針變量
  • malloc(...)動態分配一個新的char數組並返回一個指向該數組的指針
  • answer = malloc(...)將該指針復制到answer ,現在answer指向malloc分配的數組
  • answer = input另一個指針(我們在上面已經看到)復制到answer
  • 現在answerinput點到同一個字符串中, malloc的結果被遺忘並丟棄

所以這解釋了為什么你的代碼正在做你期望它做的事情。 而不是擁有字符串“Hello world”的兩個相同副本。 你只有一個字符串和兩個不同的指針,這看起來就像醫生命令的那樣。 但是一旦我們做一些稍微復雜的事情,它就會崩潰,例如,像這樣的代碼

char *lineArray[MAX_LINES];
char buffer[BUF_LEN];
int i = 0;
while (i < MAX_LINES && fgets(buffer, BUF_LEN, stdin)) {
   lineArray[i++] = copyStr(buffer);
}

最終將導致stringArray的每個元素都指向同一個字符串,而不是指向從stdin獲取的一堆不同的行。

好的,所以現在我們已經確定answer = input復制一個指針。 但是我們想復制一個剛剛分配空間的數組? 我們如何做到這一點?

由於我們的 arrays 可能是 NUL 終止的字符串,我們可以使用為復制 NUL 終止的字符串而設計的標准庫 function。

strcpy(answer, input);

對於其他 arrays 我們可以使用memcpy 主要區別在於我們必須傳遞數組長度。

memcpy(answer, input, inputLength + 1);

這兩種變體都適用於我們的情況,但首選第一種,因為它重申了我們正在處理字符串。 這是完整的固定copyStr

char* copyStr(char* input) {
  int inputLength;
  char *answer;

  inputLength = strlen(input);

  answer = malloc(inputLength + 1);
  strcpy(answer, input);

  return answer;
}

順便說一句,它與非標准但廣泛使用的strdup function 幾乎相同(strdup 具有更好的簽名和工作錯誤檢查,我們在這里省略了)。

暫無
暫無

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

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