C語言中strcpy(char *strDest, const char *strScr)字符串復制庫函數的理解與分析


1.原版的strcpy()函數原型

 

char * strcpy( char *strDest, const char *strSrc )
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest;
 while( (*strDest++ = * strSrc++) != ‘\0’ );
 return address;
}

在庫函數中,字符的賦值所采用的循環代碼,只用了一行代碼:while( (*strDest++ = * strSrc++) != ‘\0’ );。It is so beautiful !

 

2.有哪些問題

 

  從source 往dest里賦值時,如果dest的長度大於source 的長度,會發生什么情況呢。

 

  可是有時候我們會不小心把*strScr的長度大於*strDest的長度了 這時會有什么效果呢
就比如:

 char str_dest[10];
 char str_scr[20];
 strcpy(str_dest, str_scr);

有位大哥做了驗證:程序並不會報錯,它會繼續輸出str_scr中的內容;而我的解釋是,這是系統依賴的錯誤

舉個簡單的例子:

  # include <stdio.h>
  # include <string.h>
  int main()
  {
      char str1[3];
      char str2[20]={"this is a test"};
      strcpy(str1, str2);
      printf("%s\n", str1);
      return 0;
  }

 運行結果:

  this is a test
  這時我們會把目光注視到原函數上,我們發現strcpy的原函數並沒有加兩個字符串長度的限制條件,它只是把原字符串中的內容一個一個地賦值到目標字符串中,而且到最后還給目標字符串加上了結束符“\0”。那么如果目標字符串長度不夠時會怎么樣呢大哥認為:

  它會繼續一個一個地賦值字符。拿上面的例子 str1[3], str2[20]="this is a test", str2往str1里賦值。當str2里的thi 到s 的時候,str1的長度不夠了,但是數字中的地址是連續的,比如str1的首地址是1000,那么str1[0]=1000,str1[1]=1001,str1[2]=1002, 這個時候還要繼續往str1里賦值,怎么辦,地址還會繼續增加的,那么繼續增加的地址是我們沒有申請的空間的,這樣的話就會很危險的,如果沒有申請的地址空間沒有被系統占用還好,如果被系統占用的話系統可能就會崩潰的,所以在使用strcpy函數時要小心謹慎, 原字符串長度要小於目標字符串的長度。

 

3.ANSI C 中strcpy 的安全版本: strncpy

 

  在 ANSI C 中,strcpy 的安全版本是 strncpy

char *strncpy(char *s1, const char *s2, size_t n);

但 strncpy 其行為是很詭異的(不符合我們的通常習慣)。標准規定 n 並不是 sizeof(s1),而是要復制的 char 的個數。一個最常見的問題,就是 strncpy 並不幫你保證 '\0'結束。

char buf[8];
strncpy( buf, "abcdefgh", 8 );

看這個程序,buf 將會被 "abcdefgh" 填滿,但卻沒有  '\0'結束符了。

  

  另外,如果 s2 的內容比較少,而 n 又比較大的話,strncpy 將會把之間的空間都用 '\0' 填充。這又出現了一個效率上的問題,如下:

char buf[80];
strncpy( buf, "abcdefgh", 79 );

上面的 strncpy 會填寫 79 個 char,而不僅僅是 "abcdefgh" 本身。

 

strncpy 的標准用法為:(手工寫上 '\0' )

strncpy(path, src, sizeof(path) - 1);
path[sizeof(path) - 1] = '/0';
len = strlen(path);

 

4.有關strcpy的面試題參考 這里 :

 

  很多公司的面試官在面試程序員的時候,要求應聘者寫出庫函數strcpy()的工作方式或者叫實現,很多人以為這個題目很簡單,實則不然,別看這么一個小小的函數,他可以從三個方面來考查:

(1)編程風格
(2)出錯處理
(3)算法復雜度分析(用於提高性能)


如果這個題目10分的話,我們列出2分到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’ )
NULL;
  return address;//引用返回地址,方便鏈式操作!!
}

 

  從2分到10分的幾個答案我們可以清楚的看到,小小的strcpy竟然暗藏着這么多玄機,真不是蓋的!需要多么扎實的基本功才能寫一個完美的strcpy啊!

看了不同分值的strcpy版本,應該也可以寫出一個10分的strlen函數了,
完美的版本為:

int strlen( const char *str ) //輸入參數const

{
 assert( strt != NULL ); //斷言字符串地址非0
 int len;
 while( (*str++) != '\0' )
 {
  len++;
 }
 return len;
}

  

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM