面試中常問的strcpy()函式相關【溢位、手寫、返回值】
技術標籤:C++語言特性
目錄
標準庫函式中的strcpy()原始碼
strcpy是一種C語言的標準庫函式。strcpy是string copy 字串複製的縮寫。
char *strcpy(char *strDest, const char *strSrc)簡單的理解:源字串strSrc的內容複製給目標字串strDest。
更詳細的:strcpy把從strSrc地址開始且含有’\0’結束符的字串複製到以strDest開始的地址空間
#include <assert.h>
char *strcpy(char *strDest, const char *strSrc){
assert((strDest!=NULL) && (strSrc !=NULL));
char *address = strDest;
while( (*strDest++ = *strSrc++) != '\0' ) ;
return address ;
}
程式碼分析:
源字串引數用const修飾,防止源字串被修改;
assert()是斷言巨集,作用是如果它的條件返回錯誤,則終止程式執行。也就是說strcpy傳入的引數地址不允許是NULL。
while( (*strDest++ = *strSrc++) != '\0' ) ; 是字串複製過程, 到 '\0' 終止。
strcpy()的用法
用法如下程式碼所示:
char a[100]="abcdefg";
char b[100]="x";
char c[100]="y";
strcpy(b,a);
strcpy(c,a+5);
cout<<a<<endl<<b<<endl<<c<<endl;
簡單的理解:將字元陣列a的內容被複制到b中,所以 b = "abcdefg" 。
strcpy()的問題
strcpy()不是一個安全的函式,在strcpy()使用說明中有這樣兩句話:
- strcpy複製字串時不執行溢位檢查。(也就是緩衝區溢位問題)
- 如果源字串和目標字串重疊,strcpy的行為是未定義的。(也就是記憶體重疊問題)
緩衝區溢位問題
我們先通過下面的例子來看緩衝區溢位問題。
int main() {
char a[]="123";
char b[]="ggggggggggggggggggggggggggggggggggggggggg";
char c[]="xyz";
char d[]="456";
strcpy(c,b);
cout<<a<<endl<<b<<endl<<c<<endl<<d<<endl;
}
其執行結果如下:
a,b,c,d 均為字元陣列,且記憶體空間都分配在棧區,因為其都沒有長度說明,所以系統在自動分配一定的緩衝區給他們。
當執行b複製給c的操作時,由於strcpy()不執行溢位檢查,而b的長度過長,會覆蓋掉緩衝區內的其他字元陣列。
如結果所示,因為棧區是先進後出的順序,存放順序是dcba,所以覆蓋掉了字元陣列a。
記憶體重疊問題
再通過例子看記憶體重疊問題。
char a[]="123";
char b[]="ggggggggggggggggggggggggggggggggggggggggg";
char c[]="xyz";
char d[]="456";
int main() {
strcpy(c,b);
cout<<a<<endl<<b<<endl<<c<<endl<<d<<endl;
}
其執行結果如下:
同樣的變數定義,同樣的操作,但是這次字元陣列被定義為全域性變數,所以字串記憶體空間在靜態儲存區的字串常量區。
存放順序為a,b,c,d。因為執行strcpy(c,b)後導致c和d的記憶體有重疊,所以d被覆蓋了。
總結
呼叫strcpy()函式必須要注意:源字串和目標字串所指記憶體區域不可以重疊,且目標字串必須有足夠的空間來容納源字串的複製字元。
對於上述兩個例子來說,只要把字元陣列的長度固定好,就不會發生錯誤。
手寫實現strcpy()
char* strcpy(char* dest,const char* src){
if (dest==NULL || src==NULL) throw;
char* address=dest;
while (*src!='\0'){
*dest=*src;
dest++;
src++;
}
*dest=*src; //最後的'\0'也要複製過去
return address;
}
這裡我們手寫實現strcpy(),把比較難理解的那個while迴圈拆開寫。
對源字串和目標字串都進行了NULL的特判。
還值得注意的是,複製字串一定要把最後的 '\0' 一併複製過去。
strcpy()為什麼返回指標?
char *strcpy(char *strDest, const char *strSrc)
這個返回值的目的是可以使strcpy用在鏈式表示式中,增加靈活性。
如:len=strlen(strcpy(s2,s1+1)); //從s1的第二個字bai符開始複製內容到s2,並且計算出s2的長度
函式strncpy()
char* strncpy(char*, const char*, size_t)也是字串的複製函式,其有3個引數,分別是目標串,源串和整數n。
它以第三個引數n為拷貝結束標誌。
- 如果源串的長度小於N,則剩餘的字元全部用NULL填充;
- 如果源串的長度大於N,則從源串中擷取前N個字元,拷貝過去(會包括 '\0' )。
- 如果源串的長度等於N,則把源串N個字元拷貝過去,但是不包括 '\0' 。
這裡要注意的是,'\0'的複製要看n的大小,並且還需要注意NULL填充問題。
所以,strncpy()也不是絕對安全的,也要小心使用。