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

C語言輸入輸出深度剖析:從 printf 到 gets 的正確姿勢

     編輯:關於C語言

C 語言標準庫提供了豐富的 I/O 函數族,但「靈活」往往伴隨著「危險」。本文系統梳理字符串輸入輸出的主流函數,對比其行為差異,並重點揭示 gets 的安全隱患與現代替代方案。

一、輸出函數對比

函數原型特點適用場景
putsint puts(const char *s)僅輸出字符串,自動換行,效率高簡單文本輸出
printfint printf(const char *fmt, ...)格式化輸出,支持 %s 等轉換符,不自動換行混合類型、格式化報表
// puts 自動換行演示
puts("Hello");      // 輸出: Hello\n
puts("World");      // 輸出: World\n

// printf 需手動換行
printf("Hello\n");
printf("World\n");

二、輸入函數深度解析

1. gets(char *s) —— 已棄用,極度危險

  • 行為:從 stdin 讀取直到換行符或 EOF,不檢查緩衝區邊界。
  • 隱患:輸入超過緩衝區大小導致棧緩衝區溢出,是經典漏洞溫床。
  • C11 標準已移除,現代編譯器會報警告/錯誤。

2. fgets(char *s, int n, FILE *stream) —— 推薦標準

  • 行為:最多讀取 n-1 字符,遇換行符停止並保留 \n,末尾自動加 \0。
  • 安全:強制邊界檢查,防止溢出。
  • 通用:可讀文件、標準輸入、管道等任意流。
char buf[81];
if (fgets(buf, sizeof(buf), stdin)) {
    // 移除末尾換行符(若存在)
    size_t len = strlen(buf);
    if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
    printf("輸入: %s\n", buf);
}

3. scanf 族 —— 格式化輸入,但易殘留換行符

  • scanf("%80s", buf):跳過空白字符,讀取單詞(遇空格/換行停止),不會讀取整行。
  • scanf("%80[^\n]", buf):掃描集,讀取整行(除換行符外),但首次調用前需處理殘留換行符。

三、經典陷阱與修正

陷阱 1:指針未初始化直接傳給 gets

// 錯誤
char *ptr;
gets(ptr);  // ptr 野指針,崩潰

// 正確
char buf[256];
fgets(buf, sizeof(buf), stdin);

陷阱 2:scanf 後殘留換行符影響後續 fgets

int n; 
scanf("%d", &n);  // 輸入 123\n,緩衝區剩 \n
char line[100];
fgets(line, sizeof(line), stdin); // 直接讀到 \n,line 為空串

// 修正:清空輸入緩衝區
int c; while ((c = getchar()) != '\n' && c != EOF);

四、最佳實踐清單

  1. 整行輸入:優先 fgets + 手動去換行。
  2. 單詞/格式化輸入:scanf 族,但務必檢查返回值(匹配項數)。
  3. 二進制/任意數據:fread / fwrite。
  4. 錯誤處理:檢查返回值是否為 NULL/EOF,區分 EOF 與讀取錯誤(ferror)。
  5. 禁用 gets:編譯選項 -D_FORTIFY_SOURCE=2、靜態分析工具強制攔截。

五、完整示例:安全的交互式輸入

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_LINE 256

int main(void) {
    char input[MAX_LINE];
    
    puts("請輸入一行文字 (Ctrl+D/EOF 結束):");
    
    while (fgets(input, sizeof(input), stdin)) {
        // 去除末尾換行
        input[strcspn(input, "\n")] = '\0';
        
        printf("您輸入: %s\n", input);
        printf("長度: %zu\n", strlen(input));
        puts("--- 請繼續輸入 ---");
    }
    
    puts("輸入結束。");
    return 0;
}

掌握 C 語言 I/O 的精髓,在於「尊重緩衝區邊界」與「理解流的行為」。拒絕 gets,擁抱 fgets,是寫出健壯 C 代碼的第一步。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved