複製字串
指標賦值不復制字串
你可以使用 =
運算子複製整數,但不能使用 =
運算子複製 C 中的字串 .C 中的字串表示為具有終止空字元的字元陣列,因此使用 =
運算子只會儲存地址(指標)一個字串。
#include <stdio.h>
int main(void) {
int a = 10, b;
char c[] = "abc", *d;
b = a; /* Integer is copied */
a = 20; /* Modifying a leaves b unchanged - b is a 'deep copy' of a */
printf("%d %d\n", a, b); /* "20 10" will be printed */
d = c;
/* Only copies the address of the string -
there is still only one string stored in memory */
c[1] = 'x';
/* Modifies the original string - d[1] = 'x' will do exactly the same thing */
printf("%s %s\n", c, d); /* "axc axc" will be printed */
return 0;
}
上面的例子編譯是因為我們使用的是 char *d
而不是 char d[3]
。使用後者會導致編譯器錯誤。你無法在 C 中分配陣列。
#include <stdio.h>
int main(void) {
char a[] = "abc";
char b[8];
b = a; /* compile error */
printf("%s\n", b);
return 0;
}
使用標準函式複製字串
strcpy()
要真正複製字串, strcpy()
功能在 string.h
中可用。複製前必須為目的地分配足夠的空間。
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "abc";
char b[8];
strcpy(b, a); /* think "b special equals a" */
printf("%s\n", b); /* "abc" will be printed */
return 0;
}
Version => C99
snprintf()
為避免緩衝區溢位,可以使用 snprintf()
。它不是效能最佳的解決方案,因為它必須解析模板字串,但它是唯一的緩衝區限制安全函式,用於複製標準庫中易於使用的字串,無需任何額外步驟即可使用。
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "012345678901234567890";
char b[8];
#if 0
strcpy(b, a); /* causes buffer overrun (undefined behavior), so do not execute this here! */
#endif
snprintf(b, sizeof(b), "%s", a); /* does not cause buffer overrun */
printf("%s\n", b); /* "0123456" will be printed */
return 0;
}
strncat()
第二個選項,具有更好的效能,是使用 strncat()
( strcat()
的緩衝區溢位檢查版本) - 它需要第三個引數,告訴它要複製的最大位元組數:
char dest[32];
dest[0] = '\0';
strncat(dest, source, sizeof(dest) - 1);
/* copies up to the first (sizeof(dest) - 1) elements of source into dest,
then puts a \0 on the end of dest */
注意這個配方使用 sizeof(dest) - 1
; 這是至關重要的,因為 strncat()
總是新增一個空位元組(好),但不計算字串的大小(混淆和緩衝區覆蓋的原因)。
另請注意,替代方案 - 在非空字串後連線 - 更加令人擔憂。考慮:
char dst[24] = "Clownfish: ";
char src[] = "Marvin and Nemo";
size_t len = strlen(dst);
strncat(dst, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
輸出是:
23: [Clownfish: Marvin and N]
但請注意,指定為長度的大小不是目標陣列的大小,而是剩餘的空間量,不包括終結空位元組。這可能會導致大量覆蓋問題。這也有點浪費; 要正確指定長度引數,你知道目標中資料的長度,因此你可以在現有內容的末尾指定空位元組的地址,從而節省 strncat()
重新掃描它:
strcpy(dst, "Clownfish: ");
assert(len < sizeof(dst) - 1);
strncat(dst + len, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
這產生與以前相同的輸出,但是 strncat()
在開始複製之前不必掃描 dst
的現有內容。
strncpy()
最後一個選項是 strncpy()
功能。雖然你可能認為它應該是第一位的,但這是一個相當具有欺騙性的功能,它有兩個主要的問題:
- 如果通過
strncpy()
複製達到緩衝區限制,則不會寫入終止空字元。 strncpy()
總是完全填充目的地,必要時使用空位元組。
(這種古怪的實現是歷史性的,最初用於處理 UNIX 檔名 )
使用它的唯一正確方法是手動確保空終止:
strncpy(b, a, sizeof(b)); /* the third parameter is destination buffer size */
b[sizeof(b)/sizeof(*b) - 1] = '\0'; /* terminate the string */
printf("%s\n", b); /* "0123456" will be printed */
即使這樣,如果你有一個很大的緩衝區,由於額外的空填充,使用 strncpy()
變得非常低效。