複製字串

指標賦值不復制字串

你可以使用 = 運算子複製整數,但不能使用 = 運算子複製 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() 功能。雖然你可能認為它應該是第一位的,但這是一個相當具有欺騙性的功能,它有兩個主要的問題:

  1. 如果通過 strncpy() 複製達到緩衝區限制,則不會寫入終止空字元。
  2. 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() 變得非常低效。