首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

《12 個有趣的 C 語言問答》評析(一):識別技術毒素,建立正確認知

     編輯:關於C語言

網絡上流傳極廣的《12 個有趣的 C 語言問答》被許多技術網站轉載,甚至被視為面試寶典。實則這是一篇充滿誤導、錯誤示範甚至危險代碼模式的「垃圾文」。本系列將逐題拆解,糾正謬誤,建立專業的 C 語言工程認知。

為什麼要批判這篇文章?

  • 代碼存在緩衝區溢出、未定義行為、內存洩露等嚴重缺陷
  • 「答案」部分包含錯誤技術結論,誤導初學者
  • 廣泛傳播導致錯誤認知在行業內擴散
  • 外文來源不等於權威,同樣需要批判性思維

第 1 題:gets() 緩衝區溢出

原文問題代碼

char buff[10];
memset(buff, 0, sizeof(buff));
gets(buff);  // 危險!無長度檢查
printf("The buffer entered is [%s]\n", buff);

嚴重缺陷

  • gets() 不檢查輸入長度,必然導致緩衝區溢出
  • C11 標準已移除 gets(),C99 標記棄用
  • memset 清零無法防禦溢出寫入

正確做法

// 方案 1:fgets(推薦)
char buff[10];
if (fgets(buff, sizeof(buff), stdin)) {
    // 移除可能的換行符
    buff[strcspn(buff, "\n")] = '\0';
    printf("Input: [%s]\n", buff);
}

// 方案 2:C11 gets_s(可選)
// char buff[10];
// gets_s(buff, sizeof(buff));

第 2 題:strcpy 緩衝區溢出攻擊

原文問題代碼

char passwd[10];
memset(passwd, 0, sizeof(passwd));
strcpy(passwd, argv[1]);  // 無長度檢查!
if (strcmp("LinuxGeek", passwd) == 0) flag = 1;

原文「答案」錯誤分析

原文聲稱:輸入超長密碼導致溢出覆蓋 flag,繞過驗證。並建議使用 strncpy()。

深層問題

  • strncpy() 不保證以 \0 結尾,可能導致字符串處理錯誤
  • 現代編譯器默認啟用棧保護(-fstack-protector),原文需關閉保護才能復現
  • 真正解決方案:邊界檢查 + 安全字符串函數

專業級修復

#define PASSWD_MAX 9  // 預留 \0 位置

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s \n", argv[0]);
        return 1;
    }
    
    // 長度預檢
    if (strlen(argv[1]) > PASSWD_MAX) {
        puts("Password too long");
        return 1;
    }
    
    char passwd[PASSWD_MAX + 1] = {0};
    strcpy(passwd, argv[1]);  // 安全:已驗證長度
    
    // 常數時間比較(防時序攻擊)
    const char *expected = "LinuxGeek";
    int match = 1;
    for (int i = 0; expected[i] || passwd[i]; i++) {
        if (expected[i] != passwd[i]) match = 0;
    }
    
    if (match) puts("Access granted");
    else puts("Access denied");
    return 0;
}

核心原則總結

工程師的批判性思維清單

  1. 驗證所有輸入:長度、格式、範圍、編碼
  2. 使用安全函數族:*_s、snprintf、strncpy_s
  3. 啟用編譯器防護:-fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wall -Wextra
  4. 靜態分析:Clang Static Analyzer、Coverity、Cppcheck
  5. 運行時檢測:AddressSanitizer (-fsanitize=address)、Valgrind

技術傳播不應以流傳廣度為準則,而應以準確性、安全性、工程質量為標尺。識別並拒絕「技術毒素」,是每個專業開發者的責任。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
原文模式風險專業替代
gets()緩衝區溢出fgets() / gets_s()
strcpy() 無檢查溢出/注入strncpy_s() / strlcpy() / 手動檢查+strcpy
sprintf()格式化溢出snprintf()
scanf("%s")無長度限制scanf("%9s") / fgets()