場景
- 在現代
C++
開發指南出來后,并不建議使用C
的某些內存不安全的字符串處理函數。那么有哪些函數不安全?
說明
-
內存安全方面,肯定是要向
Rust
看齊的。使用標準std::string
字符串類,很大情況能避免緩沖區溢出問題。 -
如果舊項目里有用到以下的這些
C
字符串函數,那么最好用std::string
改寫吧。-
strcpy
: 不要使用.strcpy
,目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。strcpy
最后會復制源緩沖區的\0
到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。 -
strcat
: 不要使用. 只是在目標緩沖區的0
位置開始添加新的字符串。目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。strcat
最后會復制源緩沖區的\0
到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。 -
sprintf
: 不要使用. 目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。sprintf最后會復制源緩沖區的
\0`到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。 -
strncpy
: 不要使用. 目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。·strncpy·最后不會復制一個結束符到目標緩沖區,如果目標緩沖區不是以0
結尾,那么在讀取目標緩沖區時也會讀取越界。 -
strncat
: 不要使用.strncat
目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。·strncat·最后會復制源緩沖區的\0
到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。
-
-
strcpy
,strcat
,strncpy
和strncat
可以使用std::string
代替;sprintf
可以使用snprintf
或者stringstream
代替。
例子
- 以下例子使用這些危險的
C
函數處理字符串,并使用std::string
和stringstream
來替換之。
#include <iostream>
#include <string.h>
#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>using namespace std;void TestUnsafeCStringFunc()
{const int kBufSize = 32;char buf[kBufSize] = {0};string str;// 30個字符const char kStr[] = "http://blog.csdn.net/infoworld";constexpr int kStrSize = sizeof(kStr) - 1;// 1. 不要使用.`strcpy`,目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。// -- `strcpy`最后會復制源緩沖區的`\0`到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。strcpy(buf, kStr);cout << "strcpy: " << buf << endl;// -- 使用`string`的代替.str.append(kStr);cout << "string: " << str << endl;// 2. 不要使用.同`strcpy`,只是在目標緩沖區的`0`位置開始添加新的字符串。目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。// -- `strcat`最后會復制源緩沖區的`\0`到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。memset(buf, 0, sizeof(buf));strcat(buf, kStr);strcat(buf, "/");cout << "strcat: " << buf << endl;// -- 使用`string`的代替.str.clear();str.append(kStr);str.append("/");cout << "string: " << str << endl;// 3. 不要使用. 目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。// -- `sprintf`最后會復制源緩沖區的`\0`到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。sprintf(buf, "%d-%.2d-%.2d", 2025, 7, 22);cout << "sprintf: " << buf << endl;// -- 方法1:使用`stringstream`代替stringstream ss;ss << 2025 << "-" << std::setw(2) << std::setfill('0') << 7 << "-"<< std::setw(2) << std::setfill('0') << 22;str = ss.str();cout << "stringstream: " << str << endl;// -- 方法2: 使用`snprintf`代替.// -- 安全,指定目標緩沖區大小,最多指定目標緩沖區大小`-1`個字符被寫入,之后會添加一個結束符。// -- 如果`buf`為`NULL`,并且目標緩沖區大小為`0`時,返回寫源字符串所需要的總大小, 不包括結束符。snprintf(buf,sizeof(buf),"%d-%.2d-%.2d", 2025, 7, 22);cout << "snprintf: " << buf << endl;auto number = snprintf(NULL,0,"%d-%.2d-%.2d", 2025, 7, 22);cout << "snprintf number: " << number << endl;auto myBuf = (char*)malloc(number+1);sprintf(myBuf,"%d-%.2d-%.2d", 2025, 7, 22);cout << "myBuf: " << myBuf << endl;free(myBuf);// 4. 不要使用. `strncpy` 目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。// -- ·strncpy·最后不會復制一個結束符到目標緩沖區,如果目標緩沖區不是以`0`結尾,那么在讀取目標緩沖區時也會讀取越界。memset(buf, 0, sizeof(buf));strncpy(buf, kStr, kStrSize);cout << "strncpy: " << buf << endl;// -- 同上,還是使用`std::string`代替。// 5. 不要使用. `strncat` 目標緩沖區沒有設置最大接收的大小,容易造成緩沖區溢出。// -- ·strncat·最后會復制源緩沖區的`\0`到目標緩沖區末尾,前提是目標緩沖器有足夠的大小。buf[kBufSize - 1] = '1'; // 測試最后添加一個`1`字符,調用`strncat`后會使用`0`把它覆蓋。strncat(buf, "/", 1);cout << "strncat: " << buf << endl;// -- 同上,還是使用`std::string`代替。
}int main()
{std::cout << "Hello World!\n";std::cout << "=========== TestUnsafeCStringFunc =========!\n";TestUnsafeCStringFunc();
}
輸出
Hello World!
=========== TestUnsafeCStringFunc =========!
strcpy: http://blog.csdn.net/infoworld
string: http://blog.csdn.net/infoworld
strcat: http://blog.csdn.net/infoworld/
string: http://blog.csdn.net/infoworld/
sprintf: 2025-07-22
stringstream: 2025-07-22
snprintf: 2025-07-22
snprintf number: 10
myBuf: 2025-07-22
strncpy: http://blog.csdn.net/infoworld
strncat: http://blog.csdn.net/infoworld/
參考
-
strcpy
-
strcat
-
strncpy
-
strncat
-
sprintf,snprintf
-
stringstream
-
string
-
C++11語言特性和標準庫
-
如何實現std::string自己的Format(sprintf)函數
-
避免使用wsprintf函數
-
setw
-
setfill