講解比較的詳細!
已知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 Arguments"; //[2]
//assert((strDest!=NULL) && (strSrc !=NULL));
char* strDestCopy = strDest; //[3]
while((*strDest++ = *strSrc++) != '\0')//[4]
;
return strDestCopy;
}
[1]
(A)不檢查指針的有效性,說明設計者不注重程序的健壯性
(B)檢查指針的有效性時使用((!strDest) || (!strSrc))或(!(strDest && strSrc)),說明對C語言中
類型隱式轉換沒有深刻認識,在本例中char*轉換為bool即是類型隱式轉換,這種功能雖然靈活,但
是更多的是導致出錯概率的增大和維護成本的增高
(C) 檢查指針的有效性時使用((strDest==0)||(strSrc==0))說明答題者不知道使用常量的好處。直接使用字面常量(如本例中的0)會
減少程序的可維護性。0雖然簡單,但程序中可能出現很多處對指針的檢查,萬一出現筆誤,編譯器不能發現,生成的程序內含邏輯
錯誤,很難排除。而使用NULL 代替0,如果出現拼寫錯誤,編譯器就會檢查出來。
[2]
(A)return new string("Invalid arguments");,說明答題者根本不知道返回值得用途,并且它對內存泄露也沒有警惕心,從函數體中
返回函數體內分配的內存是十分危險的做法,他把釋放內存的義務拋給不知情的調用者,絕大多數情況下,調用者不會釋放內存,這
導致內存泄露
(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 *返回,類型不符,編譯報錯。
1.引言
本文的寫作目的并不在于提供C/C++程序員求職面試指導,而旨在從技術上分析面試題的內涵。文中的大多數面試題來自各大論
壇,部分試題解答也參考了網友的意見。
許多面試題看似簡單,卻需要深厚的基本功才能給出完美的解答。企業要求面試者寫一個最簡單的strcpy函數都可看出面試者在
技術上究竟達到了怎樣的程 度,我們能真正寫好一個strcpy函數嗎?我們都覺得自己能,可是我們寫出的strcpy很可能只能拿到10分
中的2分。讀者可從本文看到strcpy 函數從2分到10分解答的例子,看看自己屬于什么樣的層次。此外,還有一些面試題考查面試者敏
捷的思維能力。
分析這些面試題,本身包含很強的趣味性;而作為一名研發人員,通過對這些面試題的深入剖析則可進一步增強自身的內功。
2.找錯題
試題1:
void test1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
試題2:
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1 = 'a';
}
strcpy( string, str1 );
}
試題3:
void test3(char* str1)
{
char string[10];
if( strlen( str1 ) <= 10 )
{
strcpy( string, str1 );
}
}
解答:
試題1字符串str1需要11個字節才能存放下(包括末尾的’\0’),而string只有10個字節的空間,strcpy會導致數組越界;
對試題2,如果面試者指出字符數組str1不能在數組內結束可以給3分;如果面試者指出strcpy(string, str1)調用使得從str1內存起
復制到string內存起所復制的字節數具有不確定性可以給7分,在此基礎上指出庫函數strcpy工作方式的給10 分;
對試題3,if(strlen(str1) <= 10)應改為if(strlen(str1) < 10),因為strlen的結果未統計’\0’所占用的1個字節。
剖析:
考查對基本功的掌握:
(1)字符串以’\0’結尾;
(2)對數組越界把握的敏感度;
(3)庫函數strcpy的工作方式,如果編寫一個標準strcpy函數的總分值為10,下面給出幾個不同得分的答案:
2分
void strcpy( char *strDest, char *strSrc )
{
while( (*strDest++ = * strSrc++) != ‘\0’ );
}
4分
void strcpy( char *strDest, const char *strSrc )
//將源字符串加const,表明其為輸入參數,加2分
{
while( (*strDest++ = * strSrc++) != ‘\0’ );
}
7分
void strcpy(char *strDest, const char *strSrc)
{
//對源地址和目的地址加非0斷言,加3分
assert( (strDest != NULL) && (strSrc != NULL) );
while( (*strDest++ = * strSrc++) != ‘\0’ );
}
10分
//為了實現鏈式操作,將目的地址返回,加3分!
char * strcpy( char *strDest, const char *strSrc )
{
assert( (strDest != NULL) && (strSrc != NULL) );
char *address = strDest;
while( (*strDest++ = * strSrc++) != ‘\0’ );
return address;
}
|