C++中strncpy函式和strncpy_s函式的使用及注意事項
https://blog.csdn.net/leowinbow/article/details/82745016
在掌握了strcpy函式和strcpy_s函式之後,我們不可避免地會談到strncpy函式和strncpy_s函式,其實這四個函式的功能幾乎一致,就是對兩個字串陣列進行復制和賦值,但是具體實現有一點點區別。
首先來說一下strncpy函式。該函式依然還是存在於標準名稱空間std內,出現的目的很簡單,對於strcpy函式,只能將兩個字串進行完整的複製和賦值,這裡就會產生一個實際應用時的問題,如果我們只需要複製某個字串的前幾個字元呢?
其實對於這個問題,我們首先可能會想到使用strcpy_s函式,因為該函式有一個長度,我們在安全函式的基礎上將長度表示成我們希望複製的長度,但是實際執行時這樣寫會出現問題,舉例如下:
- // strncpy_s.cpp
- //
- int main()
- {
- using namespace std;
- char str1[100];
- char str2[5];
- cout << "Please enter your favorite lines (Warning: No longer than 100!):\n";
-
cin.getline(str1, 100);
- strcpy_s(str2, 5, str1);
- cout << "str1 is " << endl << str1 << endl;
- cout << "while str2 is " << endl << str2 << endl;
- system("pause");
- return 0;
- }
對於以上程式碼,執行結果如下圖所示:
由執行結果可知,Buffer is too small,即告訴我們安全函式之所以安全,就是不可以這樣操作,不可以把一個長度超過x的字串陣列複製並賦值給長度為x的字串陣列。
於是由此,strncpy函式應運而生。
由於我使用的IDE是Visual Studio 2017,所以只要使用strncpy函式就會報錯並提示使用安全版本,所以這裡對於基礎版本的strncpy函式只做理論介紹。
strncpy函式的原型如下所示:
char * strncpy(char * str2, char * str1, int size);
功能就是複製str1中的內容,賦進str2中,複製的長度由size的數值決定,size的型別不一定是Int,但我們一般來說遇到的長度都是整數,所以這裡用int比較簡單。
比如說以下語句:
- char str1[5] = "abcd";
- char str2[10] = "leonardo";
- strncpy(str2, str1, 3);
因為IDE不支援基礎版本函式,所以我沒法看執行結果,就文字描述一下。
以上程式碼執行之後,strncpy函式會將str1的前3個字元的內容複製,賦到str2裡,於是str2就變成了‘a’'b'‘c’。
那麼對於strncpy_s函式,原型如下所示:
strncpy_s(char * str2, int size2, char * str1, int size1);
這裡多了一個長度,就是被複制的str2的長度,我們可以用sizeof(str2)來表示這個長度。
那麼改成使用strncpy_s函式之後,上面的程式碼就可以正確運行了。
- // strncpy_s.cpp
- //
- int main()
- {
- using namespace std;
- char str1[5] = "abcd";
- char str2[10] = "leonardo";
- cout << "str1 is " << endl << str1 << endl;
- cout << "str2 is " << endl << str2 << endl;
- strncpy_s(str2, sizeof(str2), str1, 3);
- cout << "after the copy str2 is " << endl << str2 << endl;
- system("pause");
- return 0;
- }
執行結果如下圖所示:
於是回到我們最早那個程式碼,稍作修改使用strncpy_s函式,修改後程式碼如下:
- // strncpy_s.cpp
- //
- int main()
- {
- using namespace std;
- char str1[5] = "abcd";
- char str2[10] = "leonardo";
- cout << "str1 is " << endl << str1 << endl;
- cout << "str2 is " << endl << str2 << endl;
- strncpy_s(str2, sizeof(str2), str1, 3);
- cout << "after the copy str2 is " << endl << str2 << endl;
- char str3[100];
- char str4[10];
- cout << "Please enter your favorite lines (Warning: No longer than 100!):\n";
- cin.getline(str3, 100);
- strncpy_s(str4, sizeof(str4), str3, 10);
- cout << "str3 is " << endl << str3 << endl;
- cout << "while str4 is " << endl << str4 << endl;
- system("pause");
- return 0;
- }
對於str3,它本身的長度是100,使用者輸入可以輸入小於100的任意長度的字元,但是str4沒有這麼長,它的長度只有10,所以只能把str3的前10個字元內容複製給str3。
但是上面這樣寫還是有點問題,那就是字串陣列的最後一位一定是‘\0',如果直接複製10位,無法賦進str4,因為這樣就把它最後一位的'\0'給覆蓋了,就會報錯,所以我們修改上面程式碼的strncpy_s()函式那裡,改為
strncpy_s(str4, sizeof(str4), str3, 9);
這樣就正確了。
執行結果如下圖所示:
於是就完成了我們之前所說的,希望只複製某個字串陣列的一部分給另一個字串陣列的功能了。
其實這個功能還可以延伸,假如說,我們希望複製的欄位並不是原字串最開始的字元怎麼辦呢,其實也很簡單,只需要在strncpy_s函式的第三個引數處進行操作就可以了。
比如說我們希望從第2個字元開始複製,那麼第3個引數就由"str3"改寫為"str3+1"即可。因為str3是陣列,陣列名直接使用而不帶標號,即是表示地址,所以“str3+1”即是表示str3[1]的地址了,同理"str3+5"即是表示str[5]的地址。
於是我們把上述程式的第23行,即strncpy_s()函式賦值那一句改成如下所示:
strncpy_s(str4, sizeof(str4), str3 + 1, 9);
執行結果如下圖:
從圖中可以看出,確實是從第2個字元開始複製的。
那麼接下來我們就要考慮,如果希望複製的不是連續的字元,而是分散的,比如說str3[2],str3[5],str3[8]和str3[9]這種呢?
這種情況就需要多個strncpy_s語句了,因為每一句只能進行一次複製和賦值,多次才能完成上述任務。
此時我們就需要去操作第1個引數了,即str4,因為str4也是一個數組名錶示地址,所以我們這樣分散複製時,後面幾次複製就需要從str4的str4[2]開始賦值了。
我們來看一下完整程式碼:
- // strncpy_s.cpp
- //
- int main()
- {
- using namespace std;
- char str1[5] = "abcd";
- char str2[10] = "leonardo";
- cout << "str1 is " << endl << str1 << endl;
- cout << "str2 is " << endl << str2 << endl;
- strncpy_s(str2, sizeof(str2), str1, 3);
- cout << "after the copy str2 is " << endl << str2 << endl;
- cout << endl;
- char str3[100];
- char str4[10];
- cout << "Please enter your favorite lines (Warning: No longer than 100!):\n";
- cin.getline(str3, 100);
- cout << endl;
- strncpy_s(str4, sizeof(str4), str3 + 2, 1);
- strncpy_s(str4 + 1, sizeof(str4) - 1, str3 + 5, 1);
- strncpy_s(str4 + 2, sizeof(str4) - 2, str3 + 8, 2);
- cout << "str3 is " << endl << str3 << endl;
- cout << "while str4 is " << endl << str4 << endl;
- system("pause");
- return 0;
- }
上述程式碼的第25,26,27三行都是在進行strncpy_s函式的複製,可以看到我們分了3次複製,因為str3[2],str3[5]都只能單獨複製,因為不連續,而str3[8]和str3[9]可以聯合複製,因為連續。
上述程式碼執行結果如下圖所示:
從圖中可以看到,str3[2]是‘ ’,str3[5]是'm',str3[8]是'i',str3[9]是's'。
所以這樣我們就完成了分散複製的功能了。
目前我掌握的有關strncpy函式和strncpy_s函式的使用及注意事項就是這樣,以後繼續學習遇到新的知識了會持續補充。