strcpy是如何實現的?
這是2015.9.8的一篇筆記,這裡梳理下。
開始
通過一個錯誤的使用示例來說明,程式碼如下:
// wrong case void test(){ int a = 100; char str2[10]; char pad[100] = "xxxxx"; // 5 char str1[10]; char *s = "hello world !"; int i=0; for(i=0; i<10; i++) str1[i] = 'a'; strcpy(str2, str1); printf("str1 addr: %p, pad addr : %p, str2 addr:%p \n", str1, pad, str2); printf("%s\n", str2); }
你覺得執行結果是什麼?

根據程式中的對地址的輸出,可以看到三個字串的起始地址在棧空間中是連續的,下圖畫出了不同階段的記憶體空間變遷圖,可以很容易看到問題之所在!

注: 你可以調整上述不同字串的長度,觀察不同情形下的結果。
我們會看到存在的問題有:
- 由於strcpy是以'\0'字元作為拷貝字元的終止點,這裡的str1沒有足夠的儲存空間存'\0', 所以我們就無法確定strcpy會跑到哪裡;
- 根據上述變遷圖,我們看到最終我們的源字串str1本身被改變了,這合理嗎?顯然我們不希望這樣。
自己實現?
所以,如果讓你自己實現一個strcpy(),你會如何實現?
一個完美的答案應該要考慮:
- 判斷輸入是否為空;
- source 到 dest 的複製;
- 返回引數是 char *型別,以便級聯操作;
就可以寫出下面的程式碼:
char * mystrcpy(char *dest, const char *src){ if(src == NULL || dest == NULL){ printf("%s\n", "Error!"); return NULL; } char *addr = dest; while((*dest++ = *src++) != '\0'); return dest; }
看strcpy原始碼!
strcpy 原始碼
看了strcpy的原始碼實現,實現思路是差不多的。
/* Copy SRC to DEST.*/ char * strcpy (char *dest, const char *src) { char c; char *s = (char *) src; const ptrdiff_t off = dest - s - 1; do { c = *s++; s[off] = c; } while (c != '\0'); return dest; }
strcpy 的問題
沒有問題嗎?
NO,當dest指向的儲存空間,不足以儲存src指向的字串,就會出現問題,會覆蓋其他記憶體空間的值,這是致命的。示例:
void test3(){ char str2[5]; char pad[100] = "xxxxx"; // 5 char str1[10]; char *s = "hello world !"; int i=0; for(i=0; i<9; i++) str1[i] = 'a'; str1[9] = '\0'; printf("str1 addr: %p, str2 addr:%p \n", str1, str2); printf("str2 : %s\n", str2); printf("str1 : %s\n", str1); printf("pad : %s\n", pad); printf("%s\n", "after strcpy():"); strcpy(str2, str1); // mystrcpy(str2, str1); printf("str2 : %s\n", str2); printf("str1 : %s\n", str1); printf("pad : %s\n", pad); }

我們實現的版本和庫函式strcpy都是無法解決這一點的,所以一般建議使用strncpy。
如何確保dest的有效性?
我們的版本中可以判斷非空,但是對於一個沒有初始化的指標,無論如何strcpy都是會出問題的,包括庫中的strcpy。
void test4(){ char *str2; char pad[100] = "xxxxx"; // 5 char str1[10]; int i=0; for(i=0; i<9; i++) str1[i] = 'a'; str1[9] = '\0'; strcpy(str2, str1); }

所以需要我們自己在程式中保證這些!!
為何glibc中採用s[off]的方式來複制字元?
採用 s[off] = *s++ 而不是 *d++ = *ss 可以效能更優,因為只需要移動一個指標。
總結
- 一個簡單的strcpy需要考慮很多
- strcpy是不安全的
- 儘量使用strncpy()