C/C++> strcpy, strncpy, strcpy_s, strncpy_s 聯系與區別


C++中,復制C風格字符串的方法有4種:strcpy, strncpy, strcpy_s, strncpy_s。它們有什么區別和聯系了?

1. strcpy

strcpy和strncpy是早期C庫函數,頭文件string.h。現在已經發布對應safe版本,也就是strcpy_s, strncpy_s。

strcpy 函數將 strSource(包括終止 null 字符)復制到 strDestination 指定的位置。 如果源和目標字符串重疊,則 strcpy 的行為是不確定的。

注意:strcpy不安全的原因

由於 strcpy 在復制 strSource 之前不檢查 strDestination 中是否有足夠的空間,因此它可能會導致緩沖區溢出。 因此,我們建議使用 strcpy_s。

參考微軟官方文檔strcpy、wcscpy、_mbscpy

下面的程序,編譯器將產生C4996警告/錯誤提示

char p1[] = "hello";
char* ps = new char[100];

// 如果想忽略C4996,VS2019下面,需要用編譯器指令
// #pragma warning(suppress : 4996)
strcpy(ps, p1); // C4996

2. strncpy

strncpy 函數將 strSource 的初始 計數 字符復制到 strDest 並返回 strDest。 如果 count 小於或等於 strSource 的長度,則不會自動向復制的字符串追加 null 字符。 如果 count 大於 strSource 的長度,則目標字符串將用空字符填充,最大長度為 count。 如果源和目標字符串重疊,則 strncpy 的行為是不確定的。

同strcpy一樣,strncpy也是不安全的

strncpy 不檢查 strDest 中是否有足夠的空間;這會導致緩沖區溢出的潛在原因。 Count 參數限制復制的字符數;它不是 strDest 的大小限制。

strncpy和strcpy的主要區別:strncpy多了一個參數count(第3個參數),可以指定要從strSource(源字符串)拷貝的字符個數。

參考微軟官方文檔strncpy、_strncpy_l、wcsncpy、_wcsncpy_l、_mbsncpy、_mbsncpy_l

char s[20];

strcpy_s(s, sizeof(s), "AA BB CC"); // "AA BB CC"

// 2 <= "tt"字符串長度, 將"tt"拷貝並覆蓋s[0..1], 不影響后面的元素
#pragma warning(suppress : 4996)
strncpy(s, "tt", 2); // "tt BB CC" C4996

// 3 > "rr"字符串長度, 自動添加null字符(\0)
#pragma warning(suppress : 4996)
strncpy(s, "rr", 3); // "rr" C4996

printf("%s\n", s);

3. strcpy_s

strcpy_s是strcpy的安全版本,通過第二個參數dest_size限制使用目的緩存大小,對緩存大小、源字符串長度、要使用的緩存大小都做了安全檢查,避免溢出。

strcpy_s 函數將 src 地址中的內容(包括終止 null 字符)復制到 dest 指定的位置。 目標字符串必須足夠大以保存源字符串及其結尾的 null 字符。 如果源和目標字符串重疊,則 strcpy_s 的行為不確定。

如果 dest 或 src 為空指針,或者如果目標字符串的大小 dest_size 太小,則調用無效參數處理程序,如 參數驗證中所述。 如果允許執行繼續,則當 dest 或 src 為 null 指針時,這些函數將返回 EINVAL ,並將 errno 設置為 EINVAL ; 當目標字符串過小時,它們將返回 ERANGE 並將 errno 設置為 ERANGE 。
成功執行時,目標字符串始終以 null 結尾。

參考微軟官方文檔strcpy_s、wcscpy_s、_mbscpy_s、_mbscpy_s_l

要正常使用strcpy_s進行字符串拷貝,必須要求sizeof(s1)(緩存大小) >= dest_size(限制使用目的緩存大小) > strlen(src)(源字符串長度(不包括null字符))。另外dest_size長度,理論上不能超過RSIZE_MAX。

char s1[5];
//  sizeof(s1) >= dest_size > strlen(src)
strcpy_s(s1, 4, "AA"); // "AA"

//  sizeof(s1) >= dest_size <= strlen(src)
strcpy_s(s1, 1, "AA"); // 程序異常退出 

//  sizeof(s1) >= dest_size > strlen(src)
strcpy_s(s1, sizeof(s1), "AA"); // "AA"
strcpy_s(s1, sizeof(s1), "AA B"); // "AA B"

//  dest_size <= strlen(src)
strcpy_s(s1, sizeof(s1), "AA BB"); // s1緩存大小 <= 源字符串長度(不包括null字符), 程序異常退出

// sizeof(s1) < dest_size
strcpy_s(s1, 6, "A"); // dest_size > sizeof(s1), 程序異常退出  C6386

printf("%s\n", s1);

4. strncpy_s

strcpy_s與strncpy相比較,多了第4個參數指定要拷貝的源字符串字符數。必須為null字符預留目的緩存空間。

函數嘗試將 strSource 的前 D 個字符復制到 strDest,其中 D 是 count 和 strSource 的長度。 如果這些 D 字符將放在 strDest (其大小被指定為 numberOfElements) 並且仍為 null 終止符留下空間,則會復制這些字符,並追加一個終止 null。否則, strDest[0] 設置為 null 字符,並調用無效參數處理程序,如 參數驗證中所述。

參加微軟官方文檔strncpy_s、_strncpy_s_l、wcsncpy_s、_wcsncpy_s_l、_mbsncpy_s、_mbsncpy_s_l

char dst[5];
//strncpy_s(dst, 5, "a long string", 5); // 將源字符串5個字符拷貝到目的緩存5byte長度, 沒有預留null字符空間, 程序異常退出

strncpy_s(dst, 5, "a long string", _TRUNCATE); // 截斷 "a lo" , 等價於下面的語句
strncpy_s(dst, 5, "a long string", 4); // "a lo"

printf("%s\n", dst);

總結

  • 4個函數有個共同弱點,就是如果源和目標字符串存在重疊,行為未定義;
  • 使用安全版本庫函數(_s后綴)進行字符串拷貝,不要使用不安全版本;
  • 帶_s版本字符串拷貝,總能確保拷貝后字符串以null字符結尾;
  • 需要預留null字符空間;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM