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函數的使用及注意事項就是這樣,以后繼續學習遇到新的知識了會持續補充。