題目:
已知strcpy函數的原型是:
char * strcpy(char * strDest,const char * strSrc);
1.不調用庫函數,實(shí)現strcpy函數。
2.解釋為什么要返回char *。
(一)高質(zhì)量c++編程上的答案
五、編寫(xiě)strcpy函數(10分)
已知strcpy函數的原型是
char*strcpy(char *strDest, const char *strSrc);
其中strDest是目的字符串,strSrc是源字符串。
(1)不調用C++/C的字符串庫函數,請編寫(xiě)函數 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 * 類(lèi)型的返回值?
答:為了實(shí)現鏈式表達式。 // 2分
例如 int length = strlen( strcpy( strDest,“hello world”) );
【規則
2 建議
例如字符串拷貝函數strcpy的原型:
char *strcpy(char *strDest,const char *strSrc);
strcpy函數將strSrc拷貝至輸出參數strDest中,同時(shí)函數的返回值又是strDest。這樣做并非多此一舉,可以獲得如下靈活性:
charstr[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;
}
(三)帶有異常拋出的答案和解析
解說(shuō):
1.strcpy的實(shí)現代碼
char * strcpy3(char * strDest,const char * strSrc /*[0]*/)
{
if ((NULL == strDest)||(NULL == strSrc)) //[1]
throw "Invalidargument(s)"; //[2]
char * strDestCopy=strDest; //[3]
while ((*strDest++=*strSrc++)!='\0'); //[4]
return strDestCopy;
}
/*[0]: 規則
錯誤的做法:
//[1]
(A)如果不檢查指針的有效性,說(shuō)明答題者不注重代碼的健壯性。
(B)如果檢查指針的有效性時(shí)使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),說(shuō)明答題者對C語(yǔ)言中類(lèi)型的隱式轉換沒(méi)有深刻認識。在本例中((!strDest)是將char *轉換為bool即是類(lèi)型隱式轉換,這種功能雖然靈活,但更多的是導致出錯概率增大和維護成本升高。所以C++專(zhuān)門(mén)增加了bool、true、false三個(gè)關(guān)鍵字以提供更安全的條件表達式。
(C)如果檢查指針的有效性時(shí)使用((strDest==0)||(strSrc==0)),說(shuō)明答題者不知道使用常量的好處。直接使用字面常量(如本例中的0)會(huì )減少程序的可維護性。0雖然簡(jiǎn)單,但程序中可能出現很多處對指針的檢查,萬(wàn)一出現筆誤,編譯器不能發(fā)現,生成的程序內含邏輯錯誤,很難排除。而使用NULL代替0,如果出現拼寫(xiě)錯誤,編譯器就會(huì )檢查出來(lái)。
(D)NULL == strDest是將 常量寫(xiě)在表達式的左邊,如果將表達式寫(xiě)錯了,寫(xiě)成了賦值,則馬上報錯;如果 將表達式改成 strDest ==NULL,在寫(xiě)的過(guò)程中 漏寫(xiě)了 一個(gè)=,變成了 strDest = NULL,則檢查不出錯誤來(lái),可能會(huì )出現意想不到的錯誤
//[2]
(A)return new string("Invalid argument(s)");,說(shuō)明答題者根本不知道返回值的用途,并且他對內存泄漏也沒(méi)有警惕心。從函數中返回函數體內分配的內存是十分危險的做法,他把釋放內存的義務(wù)拋給不知情的調用者,絕大多數情況下,調用者不會(huì )釋放內存,這導致內存泄漏。
(B)return 0;,說(shuō)明答題者沒(méi)有掌握異常機制。調用者有可能忘記檢查返回值,調用者還可能無(wú)法檢查返回值(見(jiàn)后面的鏈式表達式)。妄想讓返回值肩負返回正確值和異常值的雙重功能,其結果往往是兩種功能都失效。應該以?huà)伋霎惓?lái)代替返回值,這樣可以減輕調用者的負擔、使錯誤不會(huì )被忽略、增強程序的可維護性。
//[3]
(A)如果忘記保存原始的strDest值(即忘記 保留strDest的頭指針),說(shuō)明答題者邏輯思維不嚴密。
//[4]
(A)循環(huán)寫(xiě)成while (*strDest++=*strSrc++);,同[1](B)。
(B)如果循環(huán)寫(xiě)成while (*strSrc!='\0') *strDest++=*strSrc++;,說(shuō)明答題者對邊界條件的檢查不力。這樣的話(huà),就是先判斷是否為零,然后再賦值,肯定是不會(huì )把結尾零賦給strDest的,而[4]中的循環(huán)是先賦值,再判斷是否為零,是會(huì )給strDest賦上結尾零的,如此這樣,這個(gè)形式循環(huán)體結束后,strDest字符串的末尾沒(méi)有正確地加上'\0'。
要正常的話(huà)應該寫(xiě)成:
while (*strSrc!='/0') *strDest++=*strSrc++;
strDest[i] = '\0';// strDest字符串的末尾加上'\0'
這里還有說(shuō)一句: = 和 * 和right ++ 這三個(gè)符號中,* 和 右++的優(yōu)先級都是2級,是對等的,其次是 = , 由于 右++的特點(diǎn):是滯后使用,造成 會(huì )先賦值,再指針++
也就是說(shuō):*strDest++=*strSrc++;
*strDest++:會(huì )拆成 *strDest 和 strDest++兩個(gè)對等的部分
其讓人感覺(jué)到的執行順序
l *strDest = *strSrc;
l 然后才能是 strDest++,strSrc++
舉個(gè)例子如下:
#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的原始值是錯誤的。其一,源字符串肯定是已知的,返回它沒(méi)有意義。其二,不能支持形如第二例的表達式。其三,為了保護源字符串,形參用const限定strSrc所指的內容,把const char *作為char *返回,類(lèi)型不符,編譯報錯。
聯(lián)系客服