在C語言中,字符串一直都是熱點,關於strcpy函數大家都很熟悉,但是真正了解的很少,一旦用到總會報一大堆莫名其妙錯誤,今天我就來給大家詳細剖析一下strcpy函數。
雖然不能看到strcpy的內部實現,但是我們通過查閱<string.h>可以看到strcpy函數的聲明。
char * __cdecl strcpy(char *, const char *);
那個_cdecl是一個函數調用約定,暫且不討論,我們今天就來說一下strcpy指針形參加const與不加的區別,幫助大家更好使用這個函數
首先我們要理解這兩種語句有何不同
1 char *p="abcd"; 2 char str[5]="abcd";
這兩條語句都是存儲abcd字符串,但是經過編譯鏈接后,會產生不同的結果
語句1,常量字符串會保存在程序的常量區,編譯時會將該字符串在常量區的起始地址拷貝過來存在指針p中,
語句2 常量字符串也會保存在程序的常量區,但是在字符數組str初始化時i,會將字符串拷貝到str中,即數組中存儲字符串副本
即str[0]='a';str[1]='b';str[2]='c';str[3]='d';str[4]='\0';
如圖所示
我們看到在地址0x0042201C 處存儲的是字符串常量abcd,而語句1匯編指令mov dword ptr ds:[ebp-4], 0xoo42201C,作用就是把常量字符串地址存到[ebp-4]這個內存空間(即變量名為p的內存地址)
語句2則是將該常量值一個個拷貝到數組中,即字符串存儲在數據段中
那么這樣區分之后,會產生一個是否允許修改的差異,我們都知道常量區中的內容不允許修改,而數據區的內容是可讀可寫的,因此,如果我們這樣寫
1 p[0]='w'; 2 str[0]='q';
會發現 雖然語句1編譯鏈接都通過,但是運行時會報錯,這是因為語句1試圖非法修改常量區的值,而常量區是允許修改,只能讀取
而語句2則可以使程序正常運行,因為str數組只是常量字符串的一份副本,這份副本存在數據區,可以修改,而且不會影響到常量區字符串的值
明白了關於指向字符串常量的指針和存儲字符串常量的數組之間的差異后,我們接下來討論指針形參輸出型和輸入型問題
對於形參是指針類型的,我們都知道是傳址調用,也明白函數內形參的改變會影響到形參,這就涉及一個實參是否允許修改的問題
char * strcpy(char * strDest,const char * strSrc);
這個函數形參,一個是不加const修飾,一個是加上了const修飾,有何區別呢?這個函數是將strSrc指向的字符串復制到strDest中,(連同字符串結束符'\0'一起復制),
那么就是說,strDest指向的值是可以修改的,而strSRC指向的值在函數中是不允許修改的,我們把加上const修飾的參數稱為輸入型參數,即只允許讀取,不允許寫入,把不加const修飾的參數稱為輸出型參數,即可以在函數內部進行讀寫改變,從而在主調函數中看到改變。
通過剛才的探討,我們可以很容易知道如下四條語句哪些會使程序運行時出錯
char *p="1234"; char *q="abcd"; char str1[5]="6789"; char str2[5]="hijk"; strcpy(p,q);① strcpy(p,str1);② strcpy(str1,p);③ strcpy(str1,str2);④
很明顯,標號①②的語句都會運行時出錯,因為strcpy的第一個形參要求是可以寫入的,而p,q都是指向了常量區字符串的首地址,不可寫入
標號③④都是可以正常運行,但是推薦寫法③,因為寫法④設計一個隱式轉換問題,將str2轉換成了常指針了。
下面給出一個strcpy函數的實現
char * strcpy(char *strDest,const char *strSrc) { assert((strDest!=NULL)&&(strSrc!=NULL)); //斷言兩個指針都不是空指針 char *address=strDest; //函數要返回復制后的字符串首地址 while((*(strDest++)=*(strSrc++))!='\0');//連同結束符一起復制 return address; //返回復制后的字符串首地址 }
總結
1 char *p="1234";指針p指向常量區,不允許修改內容及p[0]='a'非法
2 char str[5]="abcd" 字符數組str復制了常量區字符串“abcd”的值,而字符數組是在數據段,可以進行修改及str[0]='1'合法
3 對於strcpy函數的第一個形參,是輸出型形參,因此只能是數組名,而不能是字符指針變量
4 對strcpy的第二個形參,是輸入型形參,可以是數組名或者是字符指針變量,但最好是字符指針變量