日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

函数 —— strncpy() (内存重叠) memcpy() memmove() 一个字符串拷贝给另一个字符串

發布時間:2025/10/17 编程问答 12 豆豆
生活随笔 收集整理的這篇文章主要介紹了 函数 —— strncpy() (内存重叠) memcpy() memmove() 一个字符串拷贝给另一个字符串 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
char *strncpy(char *dest, const char *src, size_t n) *strncpy(char *dest, const char *src, size_t n)

/*功能:
?* C 庫函數 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串復制到 dest,最多復制 n 個字符。當 src 的長度小>于 n 時,dest 的剩余部分將用空字節填充。*/
/*參數:
?* dest -- 指向用于存儲復制內容的目標數組。
?* src -- 要復制的字符串。
?* n -- 要從源中復制的字符數。*/
/*返回值:

?* 該函數返回最終復制的字符串。*/

說明:如果src的前n個字節不含NULL字符,則結果不會以NULL字符結束。如果src的長度小于n個字節,則以NULL填充dest直到復制完n個字節。src和dest所指內存區域不可以重疊且dest必須有足夠的空間來容納src的字符串。返回指向dest的指針。

不考慮內存重疊

int main(void) {char src[40];char dst[12];char dst_less[25];strcpy(src,"This is runoob.com");printf("strlen(src) = %d\n",strlen(src)); //strlen(src) = 18

?

誤解一:如果src 長度大于等于 n, 那么 strncpy 會拷貝 n – 1 各字符到 dest, 然后補 0?

錯,大錯特錯,罰抄上面的 DESCRIPTION ,直到看到:

if there is no null byte among the first n bytes of src, the result will not be null-terminated.

這就可能導致了不安全的因素。

如果待拷貝字符串長度大于了 n, 那么 dest 是不會有結尾字符 0 的。假設這樣一種情況:

1

2

3

char s[] =?"hello world";

strncpy(s,?"shit!", 5);

puts(s);

輸出的結果是 “shit” 還是 “shit! world” ?

這種情況只是導致了輸出結果錯誤,嚴重的,如果 dest n 字節后面一直沒有 0,那么就會導致程序段錯誤。

strncpy 最開始引入標準庫是用來處理結構體中固定長度的字符串,比如路徑名,而這些字符串的用法不同于 C 中帶結尾字 0 的字符串。所以 strncpy 的初衷并不是一個安全的 strcpy.

//src 的長度 大于 或者 等于 len的長度(18>10)//把src所指向的字符串復制到dest,最多賦值10個,注意它的結果不會以NUL字節結尾(NUL即'\0')memset(dst,'\0',sizeof(dst));strncpy(dst,src,10);printf("最終的目標字符串是:%s\n",dst);//最終的目標字符串是:This is ru

誤解二:如果 src 長度小于 n, 那么strncpy 和 strcpy 效果一樣?

錯,事實上,strncpy 還會把 dest 剩下的部分全部置為 0!

//src的長度 小于 len的長度(18<20)//dest_less數組 的剩余部分將用空字節填充 到len的長度。printf("sizeof(dst_less)=%d\n",sizeof(dst_less)); //sizeof(dst_less)=25memset(dst_less,'\0',sizeof(dst_less));strncpy(dst_less,src,20);printf("最終的目標字符串是: %s\n",dst_less);//最終的目標字符串是: This is runoob.com

一直認為 strncpy 只是比 strcpy 多了長度校驗,確不知道 strncpy 會把剩下的部分全置為 0(粗體部分)。

char *strncpy(char *dest, const char *src, size_t n);

DESCRIPTION
The strcpy() function copies the string pointed to by src (including the terminating `\0′ character) to the array pointed to by dest. The strings may
not overlap, and the destination string dest must be large enough to receive the copy.
The strncpy() function is similar, except that not more than n bytes of src are copied. Thus, if there is no null byte among the first n bytes of src,
the result will not be null-terminated.
In the case where the length of src is less than that of n, the remainder of dest will be padded with null bytes.

這會導致什么后果呢?

首先,如果 strncpy 的長度填錯了,比如比實際的長,那么就可能會把其他數據清 0 了。我就遇到過這個問題,在后來檢查代碼看到這個問題時,也并不以為然,因為拷貝的字符串不可能超過緩沖區的長度。

另外,假設 dest 的長度為 1024, 而待拷貝的字符串長度只有 24,strncpy 會把余下的 1000 各字節全部置為 0. 這就可能會導致性能問題,這也是我

?

網上很多博客也寫了這個函數,面試也常常會遇到,但是,我發現網上的很多代碼都是有問題的,我們先看下大部分網上博客的實現:

[plain]?view plain?copy

  • char?*strncpy(char?*dst,?const?char?*src,?size_t?len)??
  • {??
  • ????assert(dst?!=?NULL?&&?src?!=?NULL);??
  • ????char?*res?=?dst;??
  • ????while?(len--)??
  • ????{??
  • ????????*dst++?=?*src++;??
  • ????}??
  • ????return?res;??
  • }??

  • 看著好像沒啥問題,但是,當src的長度小于len呢?這份代碼沒有處理這個問題。當src的長度小于len時,應該如何處理?《C和指針》p179給出的答案是:

    和strcpy一樣,strncpy把源字符串的字符復制到目標數組。然而,它總是正好向dst寫入len個字符。如果strlen(src)的值小于len,dst數組就用

    額外的NUL字節填充到len長度,如果strlen(src)的值大于或等于len,那么只有len個字符被復制到dst中?!?/strong>

    注意!它的結果將不會以NUL字節結尾。(NUL即‘\0’).

    ?

    由此可見,我們還需要判斷strlen(src)是否小于len,如果是,還需要在dst后面添加NUL,因此,正確的代碼應該如下:

    [plain]?view plain?copy

  • char?*strncpy(char?*dest,?const?char?*src,?size_t?len)??
  • {??
  • ????assert(dest?!=?NULL?&&?src?!=?NULL);??
  • ????char?*res?=?dest;??
  • ????int?offset?=?0;??
  • ????if?(strlen(src)?<?len)//src長度小于len??
  • ????{??
  • ????????offset?=?len?-?strlen(src);??
  • ????????len?=?strlen(src);??
  • ????}??
  • ??
  • ????while?(len--)??
  • ????{??
  • ????????*dest++?=?*src++;??
  • ????}??
  • ????while?(offset--)??
  • ????{??
  • ????????*dest++?=?'\0';??
  • ????}??
  • ????return?res;??
  • }??
  • ?

    ?

    使用這個函數,尤其需要注意,不要出現len>strlen(dst)的情況,如果len>strlen(dst),那么會破壞dst后面的內存:

    我們假設前面紅色部分是dst,然后strncpy(dst,src,10);那么后面黃色部分的內存就被破壞了。strncpy是不負責檢測len是否大于dst長度的。

    ?

    總的來說,strncpy總是復制len個字符到dst指向的內存!!!

    ?

    所以,還會出現下面的情況:

    ?

    [plain]?view plain?copy

  • char?message[]?=?"abcd";??
  • strncpy(message,?"abcde",5);??
  • cout?<<?message;??
  • 輸出是abcde燙燙燙燙燙燙燙燙燙燙燙燙燙燙 ?(結果不唯一)


    message的內存是有5個字節的,但是將abcde拷貝過去時,最后面的‘\0’被覆蓋了,strncpy并不會負責添加‘\0’到dst結尾,因此,輸出該字符串是,會在e字符后面一直找到‘\0’才結束,因此就會出現亂碼。

    考慮內存重疊

    char message[] = "qwertyu";printf("sizeof(message)=%d\n",sizeof(message)); //sizeof(message)=8strncpy(message,"abc",3);printf("message=%s\n",message); //message=abcrtyuchar *p = NULL;p=(char*)malloc(100);memcpy(p,"123456789",strlen("123456789")); //會等到錯誤的結果,有一個長度參數,只能拷>貝cnt個字節就結束了printf("before p =%s\n",p); //before p =123456789strcpy(p+1,p); //注意:這里重疊了,而strcpy是根據判斷原串中的'\0'printf("after p =%s\n",p);//after p =1123456789free(p);return(0); }

    面試中經常會遇到讓你寫一個能夠處理內存重疊的strncpy,標準庫中的strncpy是不考慮內存重疊的,如果出現內存重疊,結果將是未定義的。

    網上的很多博客也有這個代碼的實現,其實很多也是有問題的,沒有考慮src長度小于len的問題:

    [plain]?view plain?copy

  • char?*strncpy(char?*dst,?const?char?*src,?size_t?len)??
  • {??
  • ????assert(dst?!=?NULL?&&?src?!=?NULL);??
  • ????char?*res?=?dst;??
  • ????if?(dst?>=?src?&&?dst?<=?src?+?len?-?1)//重疊,從后向前復制??
  • ????{??
  • ????????dst?=?dst?+?len?-?1;??
  • ????????src?=?src?+?len?-?1;??
  • ????????while?(len--)??
  • ????????????*dst--?=?*src--;??
  • ????}??
  • ????else??
  • ????{??
  • ????????while?(len--)??
  • ????????????*dst++?=?*src++;??
  • ????}??
  • ????return?res;??
  • }??
  • ?

    那么,如果要處理內存重疊,該怎么辦?如果內存重疊和src的長度小于len這兩種情況同時出現,又如何處理?

    ?

    見圖,假設紅色部分為src,黃色為dst。如果出現內存重疊,我們很容易想到:從后往前拷貝。如果src的長度小于len,則在后面補NUL。

    ?

    [plain]?view plain?copy

  • char?*strncpy(char?*dst,?const?char?*src,?size_t?len)??
  • {??
  • ????assert(dst?!=?NULL?&&?src?!=?NULL);??
  • ????char?*res?=?dst;??
  • ????int?offset?=?0;??
  • ????char?*tmp;??
  • ????if?(strlen(src)?<?len)//src長度小于len??
  • ????{??
  • ????????offset?=?len?-?strlen(src);??
  • ????????len?=?strlen(src);??
  • ????}??
  • ??
  • ????if?(dst?>=?src?&&?dst?<=?src?+?len?-?1)//重疊,從后向前復制??
  • ????{??
  • ????????dst?=?dst?+?len?-?1;??
  • ????????src?=?src?+?len?-?1;??
  • ????????tmp?=?dst;??
  • ????????while?(len--)??
  • ????????????*dst--?=?*src--;??
  • ????}??
  • ????else??
  • ????{??
  • ????????while?(len--)??
  • ????????????*dst++?=?*src++;??
  • ????????tmp?=?dst;??
  • ????}??
  • ????while?(offset--)??
  • ????{??
  • ????????*tmp++?=?'\0';??
  • ????}??
  • ????return?res;??
  • }??

  • 那么,如果len的值大于dst的值,就會破壞dst后面的內存空間,這應該是要避免的。

    ?

    最后,我們看一個有意思的東西:(此處strncpy是考慮內存重疊的版本)

    message的長度增加了0.0 ?當然 ?,它后面的內存被破壞了,這可能帶來嚴重的后果。

    ?

    ?

    最后,使用strncpy時,最好自動添加‘\0’在結尾:

    [plain]?view plain?copy

  • char?buffer[BSIZE];??
  • .??
  • .??
  • strncpy(buffer,name,BSIZE);??
  • buffer[BSIZE-1]='\0';?
  • 解決問題:

    int main(void) {char string[8] = {'\0'};char *str1 = "abcde";int i =0;strncpy(string, str1, 3);string[3] = '\0';for(i=0;i<sizeof(string);i++){printf("sting[%d] = %c\n",i,string[i]);}printf("%s\n", string);return 0; }

    內存重疊:拷貝的目的地址在源地址范圍內。所謂內存重疊就是拷貝的目的地址和源地址有重疊。

    ?

    在函數strcpy和函數memcpy都沒有對內存重疊做處理的,使用這兩個函數的時候只有程序員自己保證源地址和目標地址不重疊,或者使用memmove函數進行內存拷貝。

    memmove函數對內存重疊做了處理。

    memmove用于從src拷貝count個字節到dest,如果目標區域和源區域有重疊的話,memmove能夠保證源串在被覆蓋之前將重疊區域的字節拷貝到目標區域中。但復制后src內容會被更改。但是當目標區域與源區域沒有重疊則和memcpy函數功能相同。

    #include <string.h> #include <stdio.h> int main() {char s[] = "Golden Global View";memmove(s,s+7,strlen(s)+1-7);printf("%s\n",s); //Global Viewreturn 0; }

    函數原型:

    1

    2

    void?*memcpy(?void?*dest,?const?void?*src, size_t count );

    void?*memmove(?void* dest,?const?void* src, size_t count );

    ?1.memcpy和memmove相同點
    都是用于從src拷貝count個字節到dest。

    2.memcpy和memmove區別
    如果目標區域和源區域有重疊的話:
    memcpy不能夠確保源串所在重疊區域在拷貝之前被覆蓋。
    memmove能夠保證源串在被覆蓋之前將重疊區域的字節拷貝到目標區域中,復制后src內容會被更改,當目標區域與源區域沒有重疊則和memcpy函數功能相同。

    但當源內存和目標內存存在重疊時,memcpy會出現錯誤,而memmove能正確地實施拷貝,但這也增加了一點點開銷。


    memmove的處理措施:
    (1)當源內存的首地址等于目標內存的首地址時,不進行任何拷貝
    (2)當源內存的首地址大于目標內存的首地址時,實行正向拷貝
    (3)當源內存的首地址小于目標內存的首地址時,實行反向拷貝

    3.Linux下的實現過程
    linux下,兩個函數都在頭文件string.h中定義,函數原型為:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    void?* __cdecl memcpy (?void?* dst,const?void?* src,size_t count);

    void?* __cdecl memmove (?void?* dst,const?void?* src,size_t count);

    ?

    //實現代碼如下:

    ?

    void?* __cdecl memcpy (?void?* dst,const?void?* src,size_t count){

    ?????????void?* ret = dst;

    ?

    ?????????while?(count--) {

    ????????// 注意, memcpy函數沒有處理dst和src區域是否重疊的問題

    ???????????????????*(char?*)dst = *(char?*)src;

    ???????????????????dst = (char?*)dst + 1;

    ???????????????????src = (char?*)src + 1;

    ?????????}

    ?

    ?????????return(ret);

    }

    ??

    void?* __cdecl memmove (?void?* dst,const?void?* src,size_t count){

    ?????????void?* ret = dst;

    ?????????if?(dst <= src || (char?*)dst >= ((char?*)src + count)) {

    ???????????????????// 若dst和src區域沒有重疊,則從起始處開始逐一拷貝

    ???????????????????while?(count--){

    ????????????????????????????*(char?*)dst = *(char?*)src;

    ????????????????????????????dst = (char?*)dst + 1;

    ????????????????????????????src = (char?*)src + 1;

    ???????????????????}

    ?????????}

    ?????????else{

    ????????// 若dst和src 區域交叉,則從尾部開始向起始位置拷貝,這樣可以避免數據沖突

    ???????????????????dst = (char?*)dst + count - 1;

    ???????????????????src = (char?*)src + count - 1;

    ?

    ???????????????????while?(count--){

    ????????????????????????????*(char?*)dst = *(char?*)src;

    ????????????????????????????dst = (char?*)dst - 1;

    ????????????????????????????src = (char?*)src - 1;

    ???????????????????}

    ?????????}

    ?

    ?????????return(ret);

    }

    當src和dst區域沒有重疊時,兩個函數是完全一樣的。否則,memcpy不能正常工作的,memmove是可以正常工作的。

    4.Windows平臺

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    #include <stdio.h>

    #include <string.h>

    ?

    /* VS2010, Windows XP, Debug模式下運行 */

    int?main(void)

    {??

    ????void?test_memfunc(void);

    ????test_memfunc();

    ?

    ????return?0;

    }

    ?

    void?test_memfunc(void){

    ?

    ????char?s1[] =?"abcdefgefghijklmnopq";//首地址:0x0012ff48

    ????char?s2[] =?"123456789";//首地址:0x0012ff34

    ????char?*c =?NULL;

    ?

    ????int?l =?sizeof(s1);//數組s1長度為21

    ?

    ?????/*

    ?????內存重疊 : s2覆蓋了原s1()的一部分空間。

    ?????即: s2(0x0012ff34-0x0012ff49)?? 的內存地址范圍和s1(0x0012ff48-0x0012ff5c)的內存地址范圍發生重疊

    ?????*/

    ?????c = memcpy(s2,s1,sizeof(s1));//改用memmove同樣運行出錯

    ?

    ?????/*

    ?????運行出錯:變量s1損壞。

    ?????Run-Time Check Failure #2 - Stack around the variable 's2' was corrupted.

    ?????*/

    ?????printf("%s",s1);

    }

    ?windows平臺下,當發生內存重疊的時候,都不能正常運行,運行棧被破壞,提示錯誤:Run-Time Check Failure #2 - Stack around the variable 's2' was corrupted.

    ?

    參考鏈接:

    //https://blog.csdn.net/sinat_30071459/article/details/72771137
    //http://www.kuqin.com/clib/string/strncpy.html
    //http://blog.haipo.me/?p=1065
    //https://www.cnblogs.com/Benoly/p/3845000.html
    //http://www.kuqin.com/clib/string/strncpy.html

    ?

    總結

    以上是生活随笔為你收集整理的函数 —— strncpy() (内存重叠) memcpy() memmove() 一个字符串拷贝给另一个字符串的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。