1. 程式人生 > >C/C++——strcpy函式的 幾種 實現 和 詳細 解析

C/C++——strcpy函式的 幾種 實現 和 詳細 解析

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

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”) );

 

【規則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 *strDestconst charstrSrc)

{

       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;//輸出為949

即使定義為#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字串了.這種方便的實現方法,看起來就是鏈子鏈在一起的,所以稱為 鏈式表示式

如此而已.呵呵.