函数 —— strncpy() (内存重叠) memcpy() memmove() 一个字符串拷贝给另一个字符串
/*功能:
?* 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 看著好像沒啥問題,但是,當src的長度小于len呢?這份代碼沒有處理這個問題。當src的長度小于len時,應該如何處理?《C和指針》p179給出的答案是: “和strcpy一樣,strncpy把源字符串的字符復制到目標數組。然而,它總是正好向dst寫入len個字符。如果strlen(src)的值小于len,dst數組就用 額外的NUL字節填充到len長度,如果strlen(src)的值大于或等于len,那么只有len個字符被復制到dst中。” 注意!它的結果將不會以NUL字節結尾。(NUL即‘\0’). ? 由此可見,我們還需要判斷strlen(src)是否小于len,如果是,還需要在dst后面添加NUL,因此,正確的代碼應該如下: [plain]?view plain?copy ? 使用這個函數,尤其需要注意,不要出現len>strlen(dst)的情況,如果len>strlen(dst),那么會破壞dst后面的內存: 我們假設前面紅色部分是dst,然后strncpy(dst,src,10);那么后面黃色部分的內存就被破壞了。strncpy是不負責檢測len是否大于dst長度的。 ? 總的來說,strncpy總是復制len個字符到dst指向的內存!!! ? 所以,還會出現下面的情況: ? [plain]?view plain?copy
|
考慮內存重疊
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 那么,如果要處理內存重疊,該怎么辦?如果內存重疊和src的長度小于len這兩種情況同時出現,又如何處理? ? 見圖,假設紅色部分為src,黃色為dst。如果出現內存重疊,我們很容易想到:從后往前拷貝。如果src的長度小于len,則在后面補NUL。 ? [plain]?view plain?copy 那么,如果len的值大于dst的值,就會破壞dst后面的內存空間,這應該是要避免的。 ? 最后,我們看一個有意思的東西:(此處strncpy是考慮內存重疊的版本) message的長度增加了0.0 ?當然 ?,它后面的內存被破壞了,這可能帶來嚴重的后果。 ? ? 最后,使用strncpy時,最好自動添加‘\0’在結尾: [plain]?view plain?copy |
解決問題:
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.memcpy和memmove相同點
當src和dst區域沒有重疊時,兩個函數是完全一樣的。否則,memcpy不能正常工作的,memmove是可以正常工作的。 4.Windows平臺
?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() 一个字符串拷贝给另一个字符串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言入门经典——基础知识(指针 数组
- 下一篇: Openldap配置TLS加密传输(完整