http://hi.baidu.com/liziyun537/blog/item/ae84671a6a75e9d9ac6e754e.html
關于sprintf和snprintf的正確使用
考慮以下有缺陷的例子:
void f(const char *p)
{
char buf[11]={0};
sprintf(buf,"%10s",p); // very dangerous
printf("%sn",buf);
}
不要讓格式標記“%10s”誤導你。如果p的長度大于10個字符,那么 sprintf() 的寫操作就會越過buf的邊界,從而產生一個緩沖區溢出。
檢測這類缺陷并不容易,因為它們只在 p 的長度大于10個字符的時候才會發生。黑客通常利用這類脆弱的代碼來入侵看上去安全的系統。
要修正這一缺陷,可以使用函數 snprintf()代替函數sprintf()。
函數原型:int snprintf(char *dest, size_t n, const char *fmt, ...);
函數說明: 最多從源串中拷貝n-1個字符到目標串中,然后再在后面加一個0。所以如果目標串的大小為n的話,將不會溢出。
函數返回值: 若成功則返回存入數組的字符數,若編碼出錯則返回負值。
推薦的用法:
void f(const char *p)
{
char buf[11]={0};
snprintf(buf, sizeof(buf), "%10s", p); // 注意:這里第2個參數應當用sizeof(str),而不要使用硬編碼11,也不應當使用sizeof(str)-1或10
printf("%sn",buf);
}
strcpy 函數操作的對象是 字符串,完成 從 源字符串 到 目的字符串 的 拷貝 功能。
snprintf 函數操作的對象 不限于字符串:雖然目的對象是字符串,但是源對象可以是字符串、也可以是任意基本類型的數據。這個函數主要用來實現 (字符串或基本數據類型)向 字符串 的轉換功能。如果源對象是字符串,并且指定 %s 格式符,也可實現字符串拷貝功能。
memcpy 函數顧名思義就是 內存拷貝,實現 將一個 內存塊 的內容復制到另一個 內存塊 這一功能。內存塊由其首地址以及長度確定。程序中出現的實體對象,不論是什么類型,其最終表現就是在內存中占據一席之地(一個內存區間或塊)。因此,memcpy 的操作對象不局限于某一類數據類型,或者說可 適用于任意數據類型,只要能給出對象的起始地址和內存長度信息、并且對象具有可操作性即可。鑒于 memcpy 函數等長拷貝的特點以及數據類型代表的物理意義,memcpy 函數通常限于同種類型數據或對象之間的拷貝,其中當然也包括字符串拷貝以及基本數據類型的拷貝。
對于字符串拷貝來說,用上述三個函數都可以實現,但是其實現的效率和使用的方便程度不同:
對于非字符串類型的數據的復制來說,strcpy 和 snprintf 一般就無能為力了,可是對 memcpy 卻沒有什么影響。但是,對于基本數據類型來說,盡管可以用 memcpy 進行拷貝,由于有賦值運算符可以方便且高效地進行同種或兼容類型的數據之間的拷貝,所以這種情況下 memcpy 幾乎不被使用。memcpy 的長處是用來實現(通常是內部實現居多)對結構或者數組的拷貝,其目的是或者高效,或者使用方便,甚或兩者兼有。
strcpy和memcpy功能上也有些差別:
比如:
const char *str1="abc\0def";
char str2[7];
首先用strcpy實現:
strcpy(str2,str1)
得到結果:str2="abc";也就是說,strcpy是以'\0'為結束標志的。
再用memcpy實現:
memset(str2,7);
memcpy(str2,str1,7);
得到結果:str2="abc\0def";
也就是說,memcpy是對內存區域的復制。當然,不僅能夠復制字符串數組,而且能夠復制整型數組等其他數組。
關于sprintf和snprintf的正確使用
考慮以下有缺陷的例子:
void f(const char *p)
{
char buf[11]={0};
sprintf(buf,"%10s",p); // very dangerous
printf("%sn",buf);
}
不要讓格式標記“%10s”誤導你。如果p的長度大于10個字符,那么 sprintf() 的寫操作就會越過buf的邊界,從而產生一個緩沖區溢出。
檢測這類缺陷并不容易,因為它們只在 p 的長度大于10個字符的時候才會發生。黑客通常利用這類脆弱的代碼來入侵看上去安全的系統。
要修正這一缺陷,可以使用函數 snprintf()代替函數sprintf()。
函數原型:int snprintf(char *dest, size_t n, const char *fmt, ...);
函數說明: 最多從源串中拷貝n-1個字符到目標串中,然后再在后面加一個0。所以如果目標串的大小為n的話,將不會溢出。
函數返回值: 若成功則返回存入數組的字符數,若編碼出錯則返回負值。
推薦的用法:
void f(const char *p)
{
char buf[11]={0};
snprintf(buf, sizeof(buf), "%10s", p); // 注意:這里第2個參數應當用sizeof(str),而不要使用硬編碼11,也不應當使用sizeof(str)-1或10
printf("%sn",buf);
}
strcpy 函數操作的對象是 字符串,完成 從 源字符串 到 目的字符串 的 拷貝 功能。
snprintf 函數操作的對象 不限于字符串:雖然目的對象是字符串,但是源對象可以是字符串、也可以是任意基本類型的數據。這個函數主要用來實現 (字符串或基本數據類型)向 字符串 的轉換功能。如果源對象是字符串,并且指定 %s 格式符,也可實現字符串拷貝功能。
memcpy 函數顧名思義就是 內存拷貝,實現 將一個 內存塊 的內容復制到另一個 內存塊 這一功能。內存塊由其首地址以及長度確定。程序中出現的實體對象,不論是什么類型,其最終表現就是在內存中占據一席之地(一個內存區間或塊)。因此,memcpy 的操作對象不局限于某一類數據類型,或者說可 適用于任意數據類型,只要能給出對象的起始地址和內存長度信息、并且對象具有可操作性即可。鑒于 memcpy 函數等長拷貝的特點以及數據類型代表的物理意義,memcpy 函數通常限于同種類型數據或對象之間的拷貝,其中當然也包括字符串拷貝以及基本數據類型的拷貝。
對于字符串拷貝來說,用上述三個函數都可以實現,但是其實現的效率和使用的方便程度不同:
- strcpy 無疑是最合適的選擇:效率高且調用方便。
- snprintf 要額外指定格式符并且進行格式轉化,麻煩且效率不高。
- memcpy 雖然高效,但是需要額外提供拷貝的內存長度這一參數,易錯且使用不便;并且如果長度指定過大的話(最優長度是源字符串長度 + 1),還會帶來性能的下降。其實 strcpy 函數一般是在內部調用 memcpy 函數或者用匯編直接實現的,以達到高效的目的。因此,使用 memcpy 和 strcpy 拷貝字符串在性能上應該沒有什么大的差別。
對于非字符串類型的數據的復制來說,strcpy 和 snprintf 一般就無能為力了,可是對 memcpy 卻沒有什么影響。但是,對于基本數據類型來說,盡管可以用 memcpy 進行拷貝,由于有賦值運算符可以方便且高效地進行同種或兼容類型的數據之間的拷貝,所以這種情況下 memcpy 幾乎不被使用。memcpy 的長處是用來實現(通常是內部實現居多)對結構或者數組的拷貝,其目的是或者高效,或者使用方便,甚或兩者兼有。
strcpy和memcpy功能上也有些差別:
比如:
const char *str1="abc\0def";
char str2[7];
首先用strcpy實現:
strcpy(str2,str1)
得到結果:str2="abc";也就是說,strcpy是以'\0'為結束標志的。
再用memcpy實現:
memset(str2,7);
memcpy(str2,str1,7);
得到結果:str2="abc\0def";
也就是說,memcpy是對內存區域的復制。當然,不僅能夠復制字符串數組,而且能夠復制整型數組等其他數組。