1. 程式人生 > 其它 >面試中常問的strcpy()函式相關【溢位、手寫、返回值】

面試中常問的strcpy()函式相關【溢位、手寫、返回值】

技術標籤:C++語言特性

目錄

標準庫函式中的strcpy()原始碼

strcpy()的用法

strcpy()的問題

緩衝區溢位問題

記憶體重疊問題

總結

手寫實現strcpy()

strcpy()為什麼返回指標?

函式strncpy()


標準庫函式中的strcpy()原始碼

strcpy是一種C語言的標準庫函式。strcpy是string copy 字串複製的縮寫。
char *strcpy(char *strDest, const char *strSrc)簡單的理解:源字串strSrc的內容複製給目標字串strDest
更詳細的:strcpy把從strSrc地址開始且含有’\0’結束符的字串複製到以strDest開始的地址空間

,返回值的型別為char*。其原始碼如下:

#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。

address地址用於記錄複製串的首地址。
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" 。

更詳細的:從a+5的地址到a的 '\0' 的內容複製到c中去,所以 c = "fg" 。

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()也不是絕對安全的,也要小心使用。