[英]Confusion in “strcat function in C assumes the destination string is large enough to hold contents of source string and its own.”
所以我讀到strcat
函數要小心使用,因為目標字符串應該足夠大,以保存自己和源字符串的內容。 我寫的以下程序也是如此:
#include <stdio.h>
#include <string.h>
int main(){
char *src, *dest;
printf("Enter Source String : ");
fgets(src, 10, stdin);
printf("Enter destination String : ");
fgets(dest, 20, stdin);
strcat(dest, src);
printf("Concatenated string is %s", dest);
return 0;
}
但對於我在這里寫的那個不是這樣的:
#include <stdio.h>
#include <string.h>
int main(){
char src[11] = "Hello ABC";
char dest[15] = "Hello DEFGIJK";
strcat(dest, src);
printf("concatenated string %s", dest);
getchar();
return 0;
}
該程序最終添加兩者而不考慮目標字符串不夠大。 為什么會這樣?
strcat
函數無法確切知道目標緩沖區的長度,因此它假定傳遞給它的緩沖區足夠大。 如果不是,則通過寫入緩沖區的末尾來調用未定義的行為 。 這就是第二段代碼中發生的事情。
第一段代碼也是無效的,因為src
和dest
都是未初始化的指針。 當它們傳遞給fgets
,它會讀取它們包含的任何垃圾值,將其視為有效地址,然后嘗試將值寫入該無效地址。 這也是未定義的行為。
使C快速的一個原因是它不會檢查以確保您遵守規則。 它只是告訴你規則,並假設你遵循它們,如果你沒有壞事,可能會或可能不會發生。 在你的特殊情況下,它似乎工作,但不能保證。
例如,當我運行你的第二段代碼時,它似乎也有效。 但如果我改成它:
#include <stdio.h>
#include <string.h>
int main(){
char dest[15] = "Hello DEFGIJK";
strcat(dest, "Hello ABC XXXXXXXXXX");
printf("concatenated string %s", dest);
return 0;
}
程序崩潰了。
我認為你的困惑實際上並不是關於strcat
的定義。 您真正的困惑是您認為C編譯器會強制執行所有“規則”。 這個假設是非常錯誤的。
是的, strcat
的第一個參數必須是一個指向內存的指針,足以存儲連接的結果。 在您的兩個程序中,都違反了該要求。 你可能會從任何一個程序中缺少錯誤消息得到這樣的印象:也許規則不是你想象的那樣,即使第一個參數不是指向足夠內存的指針,它也會以某種方式調用strcat
。 但不是,情況並非如此:當內存不足時調用strcat
肯定是錯誤的。 沒有錯誤消息,或者一個或兩個程序似乎“正常”的事實證明沒有任何證據。
這是一個類比。 (你小時候甚至可能有這種經歷。)假設你的母親告訴你不要跑到街對面,因為你可能會被車撞到。 無論如何,假設你跑到街對面,不要被車撞到。 你是否認為你母親的建議不正確? 這是一個有效的結論嗎?
總之,您閱讀的內容是正確的:必須小心使用strcat
。 但是,讓我們換一種說法:打電話時,你一定要小心strcat
。 如果你不小心,各種各樣的事情都可能出錯,沒有任何警告。 事實上,許多風格指南建議不要使用strcat
功能,因為如果你不小心它們就很容易被誤用。 (只要你小心, strcat
功能就可以完全安全地使用 - 但當然並非所有程序員都非常小心。)
確實要小心使用strcat()
函數 ,因為它不能保護您免受任何傷害。 如果源字符串不以NULL結尾,則目標字符串不以NULL結尾,或者目標字符串沒有足夠的空間, strcat
仍將復制數據。 因此,很容易覆蓋您不想覆蓋的數據。 您有責任確保有足夠的空間。 使用strncat()
而不是strcat
也會給你一些額外的安全性。
編輯這是一個例子:
#include <stdio.h>
#include <string.h>
int main()
{
char s1[16] = {0};
char s2[16] = {0};
strcpy(s2, "0123456789abcdefOOPS WAY TOO LONG");
/* ^^^ purposefully copy too much data into s2 */
printf("-%s-\n",s1);
return 0;
}
我從未分配過s1
,所以理想情況下輸出應該是--
。 但是,由於編譯器如何在內存中排列s1
和s2
,我實際得到的輸出是-OOPS WAY TOO LONG-
。 strcpy(s2,...)
覆蓋了s1
的內容。
在gcc上, -Wall
或-Wstringop-overflow
將幫助您檢測類似這樣的情況,編譯器知道源字符串的大小。 但是,通常,編譯器無法知道您的數據有多大。 因此,您必須編寫代碼,以確保您不會復制超過您的空間。
兩個片段都調用未定義的行為 - 第一個因為src
和dest
未初始化為指向任何有意義的行為,第二個因為您正在寫入數組的末尾。
C不會對數組訪問強制執行任何類型的邊界檢查 - 如果您嘗試寫入數組的末尾,則不會獲得“索引超出范圍”異常。 如果您嘗試訪問頁面邊界或重寫某些重要內容(如幀指針),則可能會出現運行時錯誤,但您只是冒着破壞程序中數據的風險。
是的,您有責任確保目標緩沖區足夠大以容納最終字符串。 否則結果是不可預測的。
我想指出第二個程序實際發生了什么,以說明問題。
它在從dest開始的內存位置分配15個字節,並將14個字節復制到其中(包括空終止符):
char dest[15] = "Hello DEFGIJK";
...在src上有11個字節,其中復制了10個字節:
char src[11] = "Hello ABC";
然后strcat()調用從src復制10個字節(9個字符加上空終止符)到dest,從dest中的'K'開始。 dest處的結果字符串將是23個字節長,包括空終止符。 問題是,你在dest中只分配了15個字節,並且與該內存相鄰的內存將被覆蓋,即損壞,導致程序不穩定,結果錯誤,數據損壞等。
請注意,strcat()函數對於您在dest(或src)上分配的內存量一無所知。 您可以確保在dest上分配了足夠的內存以防止內存損壞。
順便說一句,第一個程序根本不在dest或src分配內存,所以你對fgets()的調用會破壞從那些位置開始的內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.