簡體   English   中英

“C語言中的strcat函數混淆假設目標字符串足夠大,可以保存源字符串及其自身的內容。”

[英]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函數無法確切知道目標緩沖區的長度,因此它假定傳遞給它的緩沖區足夠大。 如果不是,則通過寫入緩沖區的末尾來調用未定義的行為 這就是第二段代碼中發生的事情。

第一段代碼也是無效的,因為srcdest都是未初始化的指針。 當它們傳遞給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 ,所以理想情況下輸出應該是-- 但是,由於編譯器如何在內存中排列s1s2 ,我實際得到的輸出是-OOPS WAY TOO LONG- strcpy(s2,...)覆蓋了s1的內容。

在gcc上, -Wall-Wstringop-overflow將幫助您檢測類似這樣的情況,編譯器知道源字符串的大小。 但是,通常,編譯器無法知道您的數據有多大。 因此,您必須編寫代碼,以確保您不會復制超過您的空間。

兩個片段都調用未定義的行為 - 第一個因為srcdest未初始化為指向任何有意義的行為,第二個因為您正在寫入數組的末尾。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM