程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 一起talk C栗子吧(第六十回:C語言實例--字符串復制)

一起talk C栗子吧(第六十回:C語言實例--字符串復制)

編輯:關於C語言

一起talk C栗子吧(第六十回:C語言實例--字符串復制)


 

各位看官們,大家好,上一回中咱們說的是字符串概述的例子,這一回咱們說的例子是:字符串復制。閒

話休提,言歸正轉。讓我們一起talk C栗子吧!

 

看官們,在C語言的標准庫中為我們提供了字符串復制函數,我們只需要包含string.h頭文件就可以使用

字符串復制函數。標准庫提供了四個字符串復制函數:strcpy,strncpy,memcpy,memmove。接下來我們

分別介紹它們的用法和使用時的注意事項。

 

strcpy函數原型:char * strcpy(char *s1, const char *s2)strcpy函數用法:它把s2中內容復制到s1中,並且返回s1.strcpy注意事項:如果s2中的內容太多,以至於超過s1的容量,那麼會覆蓋掉s1後面內存中的內容。

 

我們舉一個實際的例子來說明,在程序中定義如下字符串:

 

char s0[SIZE] = string;
char s1[SIZE]=str-1;
char s2[SIZE+3]=str-2and123;
char s3[SIZE] = {'A','B'};// other element of s3 is 
char s4[] = {'A','B'};    // there is loss of  at the end of string

 

在執行strcpy前和strcpy後分別顯示這些字符串的內存地址和字符串的內容,結果如下:

 

addr: 0xbfb08a39 | s0 : string
addr: 0xbfb08a41 | s1 : str-1
addr: 0xbfb08a51 | s2 : str-2and123
addr: 0xbfb08a49 | s3 : AB
addr: 0xbfb08a39 | s4 : ABstring

 ----- after running strcpy(s1,s3) -----
addr: 0xbfb08a39 | s0 : string
addr: 0xbfb08a41 | s1 : AB
addr: 0xbfb08a51 | s2 : str-2and123
addr: 0xbfb08a49 | s3 : AB
addr: 0xbfb08a39 | s4 : ABstring

 ----- after running strcpy(s1,s2) -----
addr: 0xbfb08a39 | s0 : string
addr: 0xbfb08a41 | s1 : str-2and123
addr: 0xbfb08a51 | s2 : str-2and123
addr: 0xbfb08a49 | s3 : 123
addr: 0xbfb08a39 | s4 : ABstring

 


從運行結果中,大家可以看到當使用strcpy(s1,s3)把s3的內容復制到s1中時,s1的內容變成了s3的

內容,其它幾個字符串的內容沒有改變。

 

當使用strcpy(s1,s2)把s2的內容復制到s1中時,s1的內容變成了s2的內容,而且s3的內容也被改變了。

我們再看看s1和s3的地址相鄰很近,肯定是在復制過程中把s1後面的內存給“污染”了,所以s3的內容

被修改了,s3就這樣中了躺槍。為了大家更加清楚地理解s3是如何中躺槍的,我們對內存中的地址進

行分析:

 

內存中的地址:0xbfb08a4 1 2 3 4 5 6 7 8 9 a b c d e f地址中的數值: s t r - 2 a n d 1 2 3

 

大家看看s1的地址從0xbfb08a41開始,占用8個字節,也就是到0xbfb08a48結束,但是執行strcpy操

作時復制了11個字符,這顯然超過了s1占有的8個字節,因此它把後面3個字節的內容也強制占用了,但是

後面3個字節的空間是系統分配給s3的,s3雖然擁有這3個字節的空間,但是裡面的內容已經被s1強制占用

時修改了。s3就這樣中了躺槍。

 

strncpy函數原型:char * strcpy(char *s1, const char *s2,size_t n)strncpy函數用法:它把s2中的n個字符復制到s1中,並且返回s1.strncpy注意事項:如果s2中的字符數量大於n,那麼只復制n個字符到s1中,而且不會在n個字符後面加上字符串的小尾巴。如果s2中的字符數量小於n,那麼只復制s2中所有的字符,不足的用空字符:進行補充,直到n個字符為止。我們一般的經驗是,在復制時設置n的值比s1的容量小於1,並且手動給s1加上小尾巴。如果n的值大於s1的容量就和strcpy一樣了。引入strncpy就是為了通過n來控制復制的內容,避免類似strcpy復制時的缺陷。因此,我們要用好n這張牌。關鍵時候來個一招招制勝,哈哈。

 

我們舉一個實際的例子來說明,程序中還使用剛才定義的字符串,在執行strncpy前和strncpy後分別顯

示這些字符串的內存地址和字符串的內容,結果如下:

 

addr: 0xbfed56a9 | s0 : string      //執行strncpy前各個字符串的值
addr: 0xbfed56b1 | s1 : str-1
addr: 0xbfed56c1 | s2 : str-2and123
addr: 0xbfed56b9 | s3 : AB
addr: 0xbfed56a9 | s4 : ABstring

----- after running strncpy(s1,s3,SIZE) -----
addr: 0xbfed56a9 | s0 : string
addr: 0xbfed56b1 | s1 : AB   //s3中字符的數量小於SIZE,所以有小尾巴
addr: 0xbfed56c1 | s2 : str-2and123
addr: 0xbfed56b9 | s3 : AB
addr: 0xbfed56a9 | s4 : ABstring

----- after running strncpy(s1,s2,SIZE) -----
addr: 0xbfed56a9 | s0 : string
addr: 0xbfed56b1 | s1 : str-2andAB  //從s2中復制了8個字符,但是沒有小尾巴
addr: 0xbfed56c1 | s2 : str-2and123
addr: 0xbfed56b9 | s3 : AB
addr: 0xbfed56a9 | s4 : ABstring

----- after running strncpy(s1,s2,SIZE-1) -----
addr: 0xbfed56a9 | s0 : string
addr: 0xbfed56b1 | s1 : str-2an  //從s2中復制了7個字符,手動加上小尾巴
addr: 0xbfed56c1 | s2 : str-2and123
addr: 0xbfed56b9 | s3 : AB
addr: 0xbfed56a9 | s4 : ABstring

----- after running strncpy(s1,s2,SIZE+3) -----
addr: 0xbfed56a9 | s0 : string
addr: 0xbfed56b1 | s1 : str-2and123  //復制的字符數量大於s1的容量,效果等於strcpy
addr: 0xbfed56c1 | s2 : str-2and123
addr: 0xbfed56b9 | s3 : 123
addr: 0xbfed56a9 | s4 : ABstring

 


我們使用strncpy(s1,s2,n)進行了四次復制操作:

第一次復制時,s2中字符的數量小於n,s1和s2的內容相同,而且給s1中的內容加上了小尾巴。第二次復制時,s2中字符的數量大於n,s1和s2中前n個字符相同,因為s1中的內容沒有小尾巴,所以它把s3中的內容也顯示了出來,因為s1和s3的地址相鄰,而且s3中的字符串有小尾巴。大家可以用我剛才分析內存中地址的方法來看,我就不詳細介紹了。第三次復制時,s2中字符的數量大於n,s1和s2中前7個字符的內容相同,我們給s1手動加上了小尾巴,這是我們推薦的做法。第四次復制時n的值大於了s1的容量,s1和s2的內容雖然相同,但是s1污染了其它的內存空間,這和strcpy的做法一樣,剛才已經分析過,這裡就不再詳細分析了。

 

接下我們說說memcpy函數:

memcpy函數原型:void * memcpy(char *s1, const char *s2,size_t n)memcpy函數用法:它把s2中的n個字符復制到s1中,並且返回s1.memcpy注意事項:和strncpy的相同,請參考上面strncpy的內容,哈哈!

memcpy的用法和strncpy類似,所以咱們就不舉例子說明了,大家可以參考上面strncpy的例子。

 

strcpy,strncpy和memcpy都有一個共同的缺點:如果s1和s2的內存空間有重疊的部分,那麼使用這些

函數就會產生意想不到的後果,這會給程序埋下“地雷”,一旦爆發後果很嚴重。想挖掉這顆雷,沒有一定的

經驗,很難找出來。為此標准庫提供了memmove。它可以避免這個缺點。

 

memmove函數原型:void * memmove(char *s1, const char *s2,size_t n)memmove函數用法:它把s2中的n個字符復制到s1中,並且返回s1.memmove注意事項:和strncpy的相同,請參考上面strncpy的內容,哈哈!如果s1和s2的內容有重疊的部分,那麼它可以很好地處理。

 

我們舉一個實際的例子來說明,程序中還使用剛才定義的字符串,執行這四個復制函數後分別顯示這些字

符串的內存地址和字符串的內容,結果如下:

 

----- after running memmove(s1,s3,SIZE) -----
addr: 0xbff8aca9 | s0 : string
addr: 0xbff8acb1 | s1 : AB   //這個是正常的復制操作,沒有任何問題
addr: 0xbff8acc1 | s2 : str-2and123
addr: 0xbff8acb9 | s3 : AB
addr: 0xbff8aca9 | s4 : ABstring

----- after running strcpy(s2,s2+1) -----
addr: 0xbff8acc1 | s2 : tr-2and123   //復制時內存有重疊,結果異常

----- after running strncpy(s2,s2+1,SIZE) -----
addr: 0xbff8acc1 | s2 : tr-2and1123  //復制時內存有重疊,結果正常

----- after running memcpy(s2,s2+1,SIZE) -----
addr: 0xbff8acc1 | s2 : tr-2and1123  //復制時內存有重疊,結果正常

----- after running memmove(s2,s2+1,SIZE) -----
addr: 0xbff8acc1 | s2 : tr-2and1123  //復制時內存有重疊,結果正常

 


對比程序運行結果,大家可以看到當復制字符串時,如果內存有重疊,那麼只有strcpy復制的結果是異常

的,其它幾個字符串復制函數的結果是正常的。其實,新版本的C庫對strncpy和momcpy做了更新 ,使他

們也可以像memmove一樣進行復制,只不過目前還沒有官方的資料來說明,因此,我們按照老的標准來介

紹這四個字符串復制函數。

 

看官們,今天的內容有些多,我們在最後對這四個復制函數做一些總結:

strcpy復制時會把字符串後面的空字符,也就是我們說的小尾巴復制上,strncpy和memcpy則不會。strcpy通常對有小尾巴的字符串進行操作,strncpy和memcpy則不管字符串是否有小尾巴,它們都可以進行操作,操作時只參考n.str開頭的復制函數,只能對字符串進行復制操作,但是mem開頭的復制函數除了對字符串進行復制操作外,還可以對其它類型的數據進行復制操作,比如自己定義的結構體類型數據。因此,它的使用范圍更加廣一些。

 

看官們,經過我們對字符串復制函數的介紹,是不是覺得標准庫也有些不可靠呢。其實,標准庫還是很可

靠的,只是有歷史方面的原因。打個比方,現在各種流行網絡用語,字典中是沒有的,流行起來後才收集

到字典中。標准庫類似我們使用的字典。先有了函數,然後才收集到標准庫中的,在收集的時候,雖然發

現了一些庫函數有缺點,但是它們已經被廣泛使用了,所以先收集起來,再提供一些改進後的庫函數。

 

看官們,我把所有的例子整理成了一個文件,並且添加了詳細的注釋。正文中就不寫代碼了,以免顯得亂,

詳細的代碼放到了我的資源中,大家可以點擊這裡下載使用。

 

各位看官,關於字符串復制的例子咱們就說到這裡。欲知後面還有什麼例子,且聽下回分解。

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved