C经典面试题之深入解析字符串拷贝的sprintf、strcpy和memcpy使用与区别
生活随笔
收集整理的這篇文章主要介紹了
C经典面试题之深入解析字符串拷贝的sprintf、strcpy和memcpy使用与区别
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、sprintf
① sprintf 定義
- sprintf 指的是字符串格式化命令,是把格式化的數(shù)據(jù)寫入某個(gè)字符串中,即發(fā)送格式化輸出到 string 所指向的字符串,直到出現(xiàn)字符串結(jié)束符 ‘\0’ 為止。sprintf 函數(shù)的聲明如下:
- 參數(shù)列表:
-
- string:這是指向一個(gè)字符數(shù)組的指針,該數(shù)組存儲(chǔ)了 C 字符串;
-
- format:這是字符串,包含了要被寫入到字符串 str 的文本,它可以包含嵌入的 format 標(biāo)簽,format 標(biāo)簽可被隨后的附加參數(shù)中指定的值替換,并按需求進(jìn)行格式化,它的標(biāo)簽屬性是 %[flags][width][.precision][length]specifier;
-
- [argument]…:根據(jù)不同的 format 字符串,函數(shù)可能需要一系列的附加參數(shù),每個(gè)參數(shù)包含了一個(gè)要被插入的值,替換了 format 參數(shù)中指定的每個(gè) % 標(biāo)簽,參數(shù)的個(gè)數(shù)應(yīng)與 % 標(biāo)簽的個(gè)數(shù)相同。
- 返回值為字符串長(zhǎng)度(strlen):
-
- 如果成功,則返回寫入的字符總數(shù),不包括字符串追加在字符串末尾的空字符;如果失敗,則返回一個(gè)負(fù)數(shù);
-
- sprintf 返回以 format 為格式,argument 為內(nèi)容組成的結(jié)果被寫入 string 的字節(jié)數(shù),結(jié)束字符‘\0’不計(jì)入內(nèi),即如果“Hello”被寫入空間足夠大的 string 后,函數(shù) sprintf 返回5。
- sprintf 是個(gè)變參函數(shù),使用 sprintf 對(duì)于寫入 buffer 的字符數(shù)是沒(méi)有限制的,這就存在了 buffer 溢出的可能性。解決這個(gè)問(wèn)題,可以考慮使用 snprintf 函數(shù),該函數(shù)可對(duì)寫入字符數(shù)做出限制。
- 相關(guān)函數(shù):
② 使用示例
- 格式化字符串:
- 拼接字符串:
- 數(shù)字轉(zhuǎn)字符串:
- 轉(zhuǎn)成 16 進(jìn)制字符串:
- 轉(zhuǎn)成 8 進(jìn)制字符串:
③ 常見問(wèn)題
- 緩沖區(qū)溢出:第一個(gè)參數(shù)的地址空間長(zhǎng)度太短,因此需要給足夠大的地址空間。當(dāng)然也可能是后面的參數(shù)的問(wèn)題,建議變參對(duì)應(yīng)一定要細(xì)心,而打印字符串時(shí),盡量使用”%.ns”的形式指定最大字符數(shù)。
- 變參對(duì)應(yīng)出問(wèn)題:通常是忘記了提供對(duì)應(yīng)某個(gè)格式符的變參,導(dǎo)致以后的參數(shù)統(tǒng)統(tǒng)錯(cuò)位,尤其是對(duì)應(yīng)”*”的那些參數(shù),都需要提供。不要把一個(gè)整數(shù)對(duì)應(yīng)一個(gè)”%s”,編譯器會(huì)編譯出錯(cuò)。
- sprintf_s 和 snprintf:sprintf_s() 是 sprintf() 的安全版本,通過(guò)指定緩沖區(qū)長(zhǎng)度來(lái)避免 sprintf() 存在的溢出風(fēng)險(xiǎn)。在使用 VS2008 時(shí)如果使用了 sprintf 函數(shù),那么編譯器會(huì)發(fā)出警告:使用 sprintf 存在風(fēng)險(xiǎn),建議使用 sprintf_s。
- strftime:sprintf 還有個(gè) strftime,專門用于格式化時(shí)間字符串的,用法與之很像,也是一大堆格式控制符,只是還要調(diào)用者指定緩沖區(qū)的最大長(zhǎng)度。
二、strcpy
① strcpy 定義
- strcpy,即 string copy(字符串復(fù)制)的縮寫。strcpy 是 C++ 語(yǔ)言的一個(gè)標(biāo)準(zhǔn)函數(shù),strcpy 把含有 ‘\0’ 結(jié)束符的字符串復(fù)制到另一個(gè)地址空間 dest,返回值的類型為 char*。
- strcpy 函數(shù)把從 src 地址開始且含有 NULL 結(jié)束符的字符串復(fù)制到以 dest 開始的地址空間,聲明如下:
- 參數(shù):
-
- dest:指向用于存儲(chǔ)復(fù)制內(nèi)容的目標(biāo)數(shù)組;
-
- src:要復(fù)制的字符串。
- 返回值:該函數(shù)返回一個(gè)指向最終的目標(biāo)字符串 dest 的指針。
- 說(shuō)明:src 和 dest 所指內(nèi)存區(qū)域不可以重疊且 dest 必須有足夠的空間來(lái)容納 src 的字符串。
② 使用示例
char src[40]; char dest[100]; memset(dest, '\0', sizeof(dest)); strcpy(src, "hello world!"); strcpy(dest, src); printf("最終的目標(biāo)字符串:%s\n", dest);// 執(zhí)行結(jié)果 最終的目標(biāo)字符串:hello world!三、memcpy
① memcpy 定義
- memcpy 指的是 C 和 C++ 使用的內(nèi)存拷貝函數(shù),功能是從源內(nèi)存地址的起始位置開始拷貝若干個(gè)字節(jié)到目標(biāo)內(nèi)存地址中,即從源 source 中拷貝 n 個(gè)字節(jié)到目標(biāo) destin 中。
- memcpy 函數(shù)的聲明為 :
- 參數(shù):
-
- destin:指向用于存儲(chǔ)復(fù)制內(nèi)容的目標(biāo)數(shù)組,類型強(qiáng)制轉(zhuǎn)換為 void* 指針;
-
- source:指向要復(fù)制的數(shù)據(jù)源,類型強(qiáng)制轉(zhuǎn)換為 void* 指針;
-
- n:要被復(fù)制的字節(jié)數(shù)。
- 返回值:該函數(shù)返回一個(gè)指向目標(biāo)存儲(chǔ)區(qū) destin 的指針。
- 說(shuō)明:
-
- source 和 destin 所指內(nèi)存區(qū)域不能重疊,函數(shù)返回指向 destin 的指針;
-
- 與 strcpy 相比,memcpy 并不是遇到 ‘\0‘ 就結(jié)束,而是一定會(huì)拷貝完 n 個(gè)字節(jié)。
② 使用示例
- 復(fù)制后覆蓋原有部分?jǐn)?shù)據(jù):
- 從 0 開始,將 s 中第 14 個(gè)字符開始的 4 個(gè)連續(xù)字符復(fù)制到 d 中:
- 將 s 中的字符串復(fù)制到字符數(shù)組 d 中:
③ 注意
- source 和 destin 所指的內(nèi)存區(qū)域可能重疊,但是如果 source 和 destin 所指的內(nèi)存區(qū)域重疊,那么這個(gè)函數(shù)并不能夠確保 source 所在重疊區(qū)域在拷貝之前不被覆蓋。而使用 memmove 可以用來(lái)處理重疊區(qū)域,函數(shù)返回指向 destin 的指針。
- 如果目標(biāo)數(shù)組 destin 本身已有數(shù)據(jù),執(zhí)行 memcpy() 后,將覆蓋原有數(shù)據(jù)(最多覆蓋 n)。如果要追加數(shù)據(jù),則每次執(zhí)行 memcpy 后,要將目標(biāo)數(shù)組地址增加到你要追加數(shù)據(jù)的地址。
- source 和 destin 都不一定是數(shù)組,任意的可讀寫的空間均可。
四、三者的區(qū)別
① 實(shí)現(xiàn)功能和操作對(duì)象不同
- strcpy 函數(shù)操作的對(duì)象是字符串 ,完成從源字符串到目的字符串的拷貝功能;
- snprintf 函數(shù)操作的對(duì)象不限于字符串:雖然目的對(duì)象是字符串,但是源對(duì)象可以是字符串,也可以是任意基本類型的數(shù)據(jù);這個(gè)函數(shù)主要用來(lái)實(shí)現(xiàn) (字符串或基本數(shù)據(jù)類型)向字符串的轉(zhuǎn)換 功能,如果源對(duì)象是字符串,并且指定 %s 格式符,也可實(shí)現(xiàn)字符串拷貝功能;
- memcpy 函數(shù)顧名思義就是內(nèi)存拷貝 ,實(shí)現(xiàn)將一個(gè) 內(nèi)存塊 的內(nèi)容復(fù)制到另一個(gè)內(nèi)存塊這一功能,內(nèi)存塊由其首地址以及長(zhǎng)度確定,程序中出現(xiàn)的實(shí)體對(duì)象,不論是什么類型,其最終表現(xiàn)就是在內(nèi)存中占據(jù)一席之地(一個(gè)內(nèi)存區(qū)間或塊)。因此,memcpy 的操作對(duì)象不局限于某一類數(shù)據(jù)類型,或者說(shuō)可適用于任意數(shù)據(jù)類型,只要能給出對(duì)象的起始地址和內(nèi)存長(zhǎng)度信息、并且對(duì)象具有可操作性即可。鑒于 memcpy 函數(shù)等長(zhǎng)拷貝的特點(diǎn)以及數(shù)據(jù)類型代表的物理意義,memcpy 函數(shù)通常限于同種類型數(shù)據(jù)或?qū)ο笾g的拷貝,其中當(dāng)然也包括字符串拷貝以及基本數(shù)據(jù)類型的拷貝。
② 實(shí)現(xiàn)的效率和使用的方便程度不同
- 對(duì)于字符串拷貝來(lái)說(shuō),用上述三個(gè)函數(shù)都可以實(shí)現(xiàn),但是其實(shí)現(xiàn)的效率和使用的方便程度不同:
-
- strcpy 無(wú)疑是最合適的選擇:效率高且調(diào)用方便;
-
- snprintf 要額外指定格式符并且進(jìn)行格式轉(zhuǎn)化,麻煩且效率不高;
-
- memcpy 雖然高效,但是需要額外提供拷貝的內(nèi)存長(zhǎng)度這一參數(shù),易錯(cuò)且使用不便;并且如果長(zhǎng)度指定過(guò)大的話(最優(yōu)長(zhǎng)度是源字符串長(zhǎng)度 + 1),還會(huì)帶來(lái)性能的下降。
-
- 其實(shí) strcpy 函數(shù)一般是在內(nèi)部調(diào)用 memcpy 函數(shù)或者用匯編直接實(shí)現(xiàn)的,以達(dá)到高效的目的。因此,使用 memcpy 和 strcpy 拷貝字符串在性能上應(yīng)該沒(méi)有什么大的差別。
- 對(duì)于非字符串類型的數(shù)據(jù)的復(fù)制來(lái)說(shuō),strcpy 和 snprintf 一般就無(wú)能為力了,可是對(duì) memcpy 卻沒(méi)有什么影響。但是,對(duì)于基本數(shù)據(jù)類型來(lái)說(shuō),盡管可以用 memcpy 進(jìn)行拷貝,由于有賦值運(yùn)算符可以方便且高效地進(jìn)行同種或兼容類型的數(shù)據(jù)之間的拷貝,所以這種情況下 memcpy 幾乎不被使用。memcpy 的長(zhǎng)處是用來(lái)實(shí)現(xiàn)(通常是內(nèi)部實(shí)現(xiàn)居多)對(duì)結(jié)構(gòu)或者數(shù)組的拷貝,其目的是或者高效,或者使用方便,甚或兩者兼有。
- 另外,strcpy 和 memcpy 功能上也有些差別:比如:const char *str1=“abc/0def”; char str2[7];
-
- 首先用 strcpy 實(shí)現(xiàn): strcpy(str2,str1) 得到結(jié)果:str2="abc"; 也就是說(shuō),strcpy是以 ‘/0’ 為結(jié)束標(biāo)志的;
-
- 再用 memcpy 實(shí)現(xiàn): memset(str2,7); memcpy(str2,str1,7); 得到結(jié)果:str2=“abc/0def”; 也就是說(shuō),memcpy是對(duì)內(nèi)存區(qū)域的復(fù)制。當(dāng)然,不僅能夠復(fù)制字符串?dāng)?shù)組,而且能夠復(fù)制整型數(shù)組等其他數(shù)組。
③ 注意
- strcpy 是一個(gè)字符串拷貝的函數(shù),它的函數(shù)原型為 strcpy(char dst, const char src) ; 將 src 開始的一段字符串拷貝到 dst 開始的內(nèi)存中去,結(jié)束的標(biāo)志符號(hào)為 ‘\0’,由于拷貝的長(zhǎng)度不是由我們自己控制的,所以這個(gè)字符串拷貝很容易出錯(cuò);
- 具備字符串拷貝功能的函數(shù)有 memcpy,這是一個(gè)內(nèi)存拷貝函數(shù),它的函數(shù)原型為 memcpy (char dst, const char src, unsigned int len) ; 將長(zhǎng)度為 len 的一段內(nèi)存,從 src 拷貝到 dst 中去,這個(gè)函數(shù)的長(zhǎng)度可控,但是會(huì)有內(nèi)存讀寫錯(cuò)誤(比如 len的長(zhǎng)度大于要拷貝的空間或目的空間);
- sprintf 是格式化函數(shù),將一段數(shù)據(jù)通過(guò)特定的格式,格式化到一個(gè)字符串緩沖區(qū)中去;sprintf 格式化的函數(shù)的長(zhǎng)度不可控,有可能格式化后的字符串會(huì)超出緩沖區(qū)的大小,造成溢出。
總結(jié)
以上是生活随笔為你收集整理的C经典面试题之深入解析字符串拷贝的sprintf、strcpy和memcpy使用与区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Git之深入解析如何贮藏工作分支与清理工
- 下一篇: Git之深入解析如何通过GPG签署和验证