C/C++——strcpy函式的 幾種 實現 和 詳細 解析
題目:
已知strcpy函式的原型是:
char * strcpy(char * strDest,const char * strSrc);
1.不呼叫庫函式,實現strcpy函式。
2.解釋為什麼要返回char *。
(一)高質量c++程式設計上的答案
五、編寫strcpy函式(10分)
已知strcpy函式的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字串,strSrc是源字串。
(1)不呼叫C++/C的字串庫函式,請編寫函式 strcpy
char *strcpy(char *strDest,
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char *address = strDest;
while( (*strDest++ = * strSrc++) != '\0' ) // 2分
NULL ;
return address ; // 2分
}
(2)strcpy能把strSrc的內容複製到strDest,為什麼還要char * 型別的返回值?
答:為了實現鏈式表達式。 // 2分
例如 int length = strlen( strcpy( strDest, “hello world”) );
【規則6-2-3】不要將正常值和錯誤標誌混在一起返回。正常值用輸出引數獲得,而錯誤標誌用return語句返回。錯誤資訊可以用throw丟擲。
2 建議6-2-1】有時候函式原本不需要返回值,但為了增加靈活性如支援鏈式表達,可以附加返回值。
例如字串拷貝函式strcpy的原型:
char *strcpy(char *strDest,const char *strSrc);
strcpy函式將strSrc拷貝至輸出引數strDest中,同時函式的返回值又是strDest。這樣做並非多此一舉,可以獲得如下靈活性:
char str[20];
int length = strlen( strcpy(str, “Hello World”) );
()
(二)程式設計師面試寶典中的答案
char* strcpy1(char *strDest, const char* strSrc)
{
assert(strSrc != NULL );
assert(strDest != NULL);
int i;
char *address = strDest;
for(i = 0; strSrc[i] != '\0'; i++)
strDest[i] = strSrc[i];
strDest[i] = '\0';
return address;
}
(三)帶有異常丟擲的答案和解析
解說:
1.strcpy的實現程式碼
char * strcpy3(char * strDest,const char * strSrc /*[0]*/)
{
if ((NULL == strDest)||(NULL == strSrc)) //[1]
throw "Invalid argument(s)"; //[2]
char * strDestCopy=strDest; //[3]
while ((*strDest++=*strSrc++)!='\0'); //[4]
return strDestCopy;
}
/*[0]: 規則6-1-3】如果引數是指標,且僅作輸入用,則應在型別前加const,以防止該指標在函式體內被意外修改。
錯誤的做法:
//[1]
(A)如果不檢查指標的有效性,說明答題者不注重程式碼的健壯性。
(B)如果檢查指標的有效性時使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),說明答題者對C語言中型別的隱式轉換沒有深刻認識。在本例中((!strDest)是將char *轉換為bool即是型別隱式轉換,這種功能雖然靈活,但更多的是導致出錯概率增大和維護成本升高。所以C++專門增加了bool、true、false三個關鍵字以提供更安全的條件表示式。
(C)如果檢查指標的有效性時使用((strDest==0)||(strSrc==0)),說明答題者不知道使用常量的好處。直接使用字面常量(如本例中的0)會減少程式的可維護性。0雖然簡單,但程式中可能出現很多處對指標的檢查,萬一出現筆誤,編譯器不能發現,生成的程式內含邏輯錯誤,很難排除。而使用NULL代替0,如果出現拼寫錯誤,編譯器就會檢查出來。
(D)NULL == strDest是將 常量寫在表示式的左邊,如果將表示式寫錯了,寫成了賦值,則馬上報錯;如果 將表示式改成 strDest ==NULL,在寫的過程中 漏寫了 一個=,變成了 strDest = NULL,則檢查不出錯誤來,可能會出現意想不到的錯誤
//[2]
(A)return new string("Invalid argument(s)");,說明答題者根本不知道返回值的用途,並且他對記憶體洩漏也沒有警惕心。從函式中返回函式體內分配的記憶體是十分危險的做法,他把釋放記憶體的義務拋給不知情的呼叫者,絕大多數情況下,呼叫者不會釋放記憶體,這導致記憶體洩漏。
(B)return 0;,說明答題者沒有掌握異常機制。呼叫者有可能忘記檢查返回值,呼叫者還可能無法檢查返回值(見後面的鏈式表示式)。妄想讓返回值肩負返回正確值和異常值的雙重功能,其結果往往是兩種功能都失效。應該以丟擲異常來代替返回值,這樣可以減輕呼叫者的負擔、使錯誤不會被忽略、增強程式的可維護性。
//[3]
(A)如果忘記儲存原始的strDest值(即忘記 保留strDest的頭指標),說明答題者邏輯思維不嚴密。
//[4]
(A)迴圈寫成while (*strDest++=*strSrc++);,同[1](B)。
(B)如果迴圈寫成while (*strSrc!='\0') *strDest++=*strSrc++;,說明答題者對邊界條件的檢查不力。這樣的話,就是先判斷是否為零,然後再賦值,肯定是不會把結尾零賦給strDest的,而[4]中的迴圈是先賦值,再判斷是否為零,是會給strDest賦上結尾零的,如此這樣,這個形式迴圈體結束後,strDest字串的末尾沒有正確地加上'\0'。
要正常的話應該寫成:
while (*strSrc!='/0') *strDest++=*strSrc++;
strDest[i] = '\0';// strDest字串的末尾加上'\0'
這裡還有說一句: = 和 * 和 right ++ 這三個符號中,* 和 右++的優先順序都是2級,是對等的,其次是 = , 由於 右++的特點:是滯後使用, 造成 會先賦值,再指標++
也就是說:*strDest++=*strSrc++;
*strDest++:會拆成 *strDest 和 strDest++兩個對等的部分
其讓人感覺到的執行順序
l *strDest = *strSrc;
l 然後才能是 strDest++,strSrc++
舉個例子如下:
#define product(x) (x * x)
int i = 3, j, k;
j = product(i++);
k = product(++i);
cout << j << " " << k << endl;//輸出為9和49
即使定義為#define product(x) ((x) * (x))得到的結果還是一樣
2.返回strDest的原始值使函式能夠支援鏈式表示式,增加了函式的“附加值”。同樣功能的函式,如果能合理地提高的可用性,自然就更加理想。
鏈式表示式的形式如:
int iLength=strlen(strcpy(strA,strB));
又如:
char * strA=strcpy(new char[10],strB);
返回strSrc的原始值是錯誤的。其一,源字串肯定是已知的,返回它沒有意義。其二,不能支援形如第二例的表示式。其三,為了保護源字串,形參用const限定strSrc所指的內容,把const char *作為char *返回,型別不符,編譯報錯。
關於鏈式表示式
已知strcpy函式的原型是 char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字串,strSrc是源字串。
(1)不呼叫C++/C的字串庫函式,請編寫函式
strcpy char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != ‘\0’ ) // 2分
NULL ;
return address ; // 2分
}
(2)strcpy能把strSrc的內容複製到strDest,為什麼還要char * 型別的返回值?
答:為了實現鏈式表示式。 // 2分
例如 int length = strlen( strcpy( strDest, “hello world”) );
--------------------------------------------------------------------------------------------------------------------------------
看到了這個詞:鏈式表示式,之前沒有聽過,所以去百度了一下,發現有人解釋的還算明白:
-------------------------------------------------------------------------------------------------------------------------------- http://topic.csdn.net/t/20061123/21/5180993.html 1,
就是方便一些,否則就要這樣寫:
char strDest[12];
strcpy( strDest, “hello world”);
int length = strlen(strDest);
什麼鏈式就是一連串寫下來的意思。。。
--------------------------------------------------------------------------------------------------------------------------------
其實,說白了,就是如果上面的字串拷貝函式strcopy的返回值是void,那麼,上面那句:
int length = strlen( strcpy( strDest, “hello world”) );
就要像上面那位的回答,寫成好幾句了:
char strDest[12];
strcpy( strDest, “hello world”);
int length = strlen(strDest);
而這種直接返回char *的手段,就是為了後來函式呼叫者方便而設計的.不用你這麼麻煩用上述方法去使用了,而直接可以使用拷貝後的dest字串了.這種方便的實現方法,看起來就是鏈子鏈在一起的,所以稱為 鏈式表示式
如此而已.呵呵.
關於鏈式表示式
已知strcpy函式的原型是 char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字串,strSrc是源字串。
(1)不呼叫C++/C的字串庫函式,請編寫函式
strcpy char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != ‘\0’ ) // 2分
NULL ;
return address ; // 2分
}
(2)strcpy能把strSrc的內容複製到strDest,為什麼還要char * 型別的返回值?
答:為了實現鏈式表示式。 // 2分
例如 int length = strlen( strcpy( strDest, “hello world”) );
--------------------------------------------------------------------------------------------------------------------------------
看到了這個詞:鏈式表示式,之前沒有聽過,所以去百度了一下,發現有人解釋的還算明白:
-------------------------------------------------------------------------------------------------------------------------------- http://topic.csdn.net/t/20061123/21/5180993.html 1,
就是方便一些,否則就要這樣寫:
char strDest[12];
strcpy( strDest, “hello world”);
int length = strlen(strDest);
什麼鏈式就是一連串寫下來的意思。。。
--------------------------------------------------------------------------------------------------------------------------------
其實,說白了,就是如果上面的字串拷貝函式strcopy的返回值是void,那麼,上面那句:
int length = strlen( strcpy( strDest, “hello world”) );
就要像上面那位的回答,寫成好幾句了:
char strDest[12];
strcpy( strDest, “hello world”);
int length = strlen(strDest);
而這種直接返回char *的手段,就是為了後來函式呼叫者方便而設計的.不用你這麼麻煩用上述方法去使用了,而直接可以使用拷貝後的dest字串了.這種方便的實現方法,看起來就是鏈子鏈在一起的,所以稱為 鏈式表示式
如此而已.呵呵.