問題是從baiyuxiong發的那個“出現頻率最高的筆試題strcpy寫法+分析zz”文章開始的:
題目:
已知strcpy函數的原型是:
char * strcpy(char * strDest,const char * strSrc);
1.不調用庫函數,實現strcpy函數。
2.解釋為什麼要返回char *。
解說:
1.strcpy的實現代碼
char * strcpy(char * strDest,const char * strSrc)
{
if ((strDest==NULL)||(strSrc==NULL)) //[1]
throw "Invalid argument(s)"; //[2]
char * strDestCopy=strDest; //[3]
while ((*strDest++=*strSrc++)!='/0'); //[4]
return strDestCopy;
}
錯誤的做法:
[1]
(A)不檢查指標的有效性,說明答題者不注重代碼的健壯性。
(B)檢查指標的有效性時使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),說明答題者
對C語言中類型的隱式轉換沒有深刻認識。在本例中char
*轉換為bool即是類型隱式轉換,這種功能雖然靈活,但更多的是導致出錯機率增大和維護成本升高。所以C++專門增加了bool、true、false
三個關鍵字以提供更安全的條件運算式。
(C)檢查指標的有效性時使用((strDest==0)||(strSrc==0)),說明答題者不知道使用常量的好處。直接使用字面常量(如本例中的
0)會減少程式的可維護性。0雖然簡單,但程式中可能出現很多處對指標的檢查,萬一出現筆誤,編譯器不能發現,產生的程式內含邏輯錯誤,很難排除。而使用
NULL代替0,如果出現拼字錯誤,編譯器就會檢查出來。
[2]
(A)return new
string("Invalid
argument(s)");,說明答題者根本不知道傳回值的用途,並且他對記憶體流失也沒有警惕心。從函數中返回函數體內分配的記憶體是十分危險的做法,他
把釋放記憶體的義務拋給不知情的調用者,絕大多數情況下,調用者不會釋放記憶體,這導致記憶體流失。
(B)return
0;,說明答題者沒有掌握異常機制。調用者有可能忘記檢查傳回值,調用者還可能無法檢查傳回值(見後面的鏈式運算式)。妄想讓傳回值肩負返回正確值和異常
值的雙重功能,其結果往往是兩種功能都失效。應該以拋出異常來代替傳回值,這樣可以減輕調用者的負擔、使錯誤不會被忽略、增強程式的可維護性。
[3]
(A)忘記儲存原始的strDest值,說明答題者邏輯思維不嚴密。
[4]
(A)迴圈寫成while (*strDest++=*strSrc++);,同[1](B)。
(B)迴圈寫成while (*strSrc!='/0') *strDest++=*strSrc++;,說明答題者對邊界條件的檢查不力。迴圈體結束後,strDest字串的末尾沒有正確地加上'/0'。
2.返回strDest的原始值使函數能夠支援鏈式運算式,增加了函數的“附加值”。同樣功能的函數,如果能合理地提高的可用性,自然就更加理想。
鏈式運算式的形式如:
int iLength=strlen(strcpy(strA,strB));
又如:
char * strA=strcpy(new char[10],strB);
返回strSrc的原始值是錯誤的。其一,源字串肯定是已知的,返回它沒有意義。其二,不能支援形如第二例的運算式。其三,為了保護源字串,形參用
const限定strSrc所指的內容,把const char *作為char *返回,類型不符,編譯報錯。
自己簡單寫了下程式:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strcpy(char *str_dest,const char *str_src)
{
char *str_destcopy;
if( (str_dest==NULL) || (str_src==NULL)){
printf("invalid parameters/n");
return NULL;
}
str_destcopy = str_dest;
while( (*str_dest++=*str_src++)!='/0');
return str_destcopy;
}
int main(int argc, char *argv[])
{
char *str_src="helloworld";
char *str_dest;
char *ret;
str_dest=(char *)malloc(sizeof(char)*10);
if(NULL!=str_dest){
memset(str_dest, 0, sizeof(char)*10);
}
printf("str_src:%s/n",str_src);
if( (ret=strcpy(str_dest, str_src)) == NULL)
printf("strcpy ERROR./n");
else
printf("str_dest:%s/n",ret);
}
有兩點注意:
1、記得要給字串指標分配記憶體:
str_dest=(char *)malloc(sizeof(char)*10);
if(NULL!=str_dest){
memset(str_dest, 0, sizeof(char)*10);
}
最開始沒有分配記憶體,總是報段錯誤
另外也採用char str_dest[10];的方式,就不用指標了
2、另外,最開始沒有加#include <stdlib.h>和#include <string.h>兩句,編譯出現的警告:
strcpy.c:23: 警告:隱式聲明與內建函數‘malloc’不相容
strcpy.c:25: 警告:隱式聲明與內建函數‘memset’不相容
後來man了一下這兩個函數,發現缺少標頭檔,包含進來後就沒有以上錯誤了
環境:linux系統,gcc4.3.2