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

PAT 甲級 1009 說反話:單詞逆序輸出的兩種 C 語言實現與編譯器差異分析

     編輯:關於C語言

單詞逆序:從數組存儲到流式掃描的兩種范式對比

題目要求:讀入一行長度 ≤ 80 的英文句子,單詞間單空格分隔,行尾無多餘空格,輸出單詞順序逆序的句子。本文提供雙數組存儲法與流式 scanf 法兩種實現,並深度分析後者依賴 EOF 行為的編譯器差異問題。

一、 方法一:雙數組存儲(通用、穩健、無編譯器依賴)

#include 
#include 

#define MAX_LEN 80
#define MAX_WORDS 40 // 最多約 40 個單字母單詞

int main() {
    char words[MAX_WORDS][MAX_LEN + 1]; // 存儲每個單詞
    char line[MAX_LEN + 1];
    int count = 0;
    
    // 讀取整行(含空格)
    if (fgets(line, sizeof(line), stdin) == NULL) return 0;
    line[strcspn(line, "\n")] = '\0'; // 移除換行符
    
    // 解析單詞:手動掃描或 sscanf
    char *token = strtok(line, " ");
    while (token != NULL && count < MAX_WORDS) {
        strcpy(words[count++], token);
        token = strtok(NULL, " ");
    }
    
    // 逆序輸出
    for (int i = count - 1; i >= 0; i--) {
        printf("%s", words[i]);
        if (i > 0) printf(" ");
    }
    printf("\n");
    return 0;
}

優點:邏輯清晰,不依賴特定輸入結束標記,可處理任意空白分隔(strtok 自動處理多空格),移植性最強。

二、 方法二:流式 scanf("%s")` + EOF` 判斷(代碼極簡但有陷阱)

#include 

#define MAX 80

int main() {
    char s[MAX/2][MAX+1]; // 假設最多 40 個單詞
    int cnt = 0;
    
    // 關鍵:scanf("%s") 跳過空白,讀取單詞;遇 EOF 返回 EOF
    while (scanf("%s", s[cnt++]) != EOF);
    
    cnt -= 2; // 循環結束時 cnt 多增 1,且最後一次讀失敗不算,實際有效 cnt-1,輸出索引從 cnt-2 開始
    while (cnt > 0) {
        printf("%s ", s[cnt--]);
    }
    printf("%s\n", s[0]); // 最後一個單詞不加空格
    return 0;
}

三、 方法二深度陷阱分析

1. EOF` 觸發條件差異

  • OJ 環境:輸入文件讀完自動觸發 EOF,scanf 返回 EOF,循環正常結束。
  • 本地終端 (Windows/Dev-C++):標準輸入不自動發送 EOF,程序會卡在 scanf 等待輸入,需手動輸入 Ctrl+Z + Enter 才能結束,體驗極差。
  • Linux/macOS 終端:Ctrl+D 發送 EOF,行為正常。

2. 緩衝區殘留換行符問題

若前序有 scanf("%d", &n) 讀取整數,緩衝區殘留 \n,後續 scanf("%s") 會跳過它(%s 自動跳過空白),通常無影響。但若混用 getchar()/gets() 需手動清理。

3. 邊界條件:單詞數超限

代碼固定 s[40][81],若輸入單詞超過 40 個(極短單詞),發生緩衝區溢出。安全做法:動態分配或檢查 cnt < MAX_WORDS。

四、 進階優化:單次掃描原地逆序(O(1) 額外空間)

#include 
#include 

void reverse(char *s, int l, int r) { // 反轉 s[l..r]
    while (l < r) { char t = s[l]; s[l++] = s[r]; s[r--] = t; }
}

int main() {
    char line[81];
    fgets(line, sizeof(line), stdin);
    line[strcspn(line, "\n")] = '\0';
    int len = strlen(line);
    
    // 1. 整體反轉
    reverse(line, 0, len - 1);
    // 2. 各單詞單獨反轉
    int start = 0;
    for (int i = 0; i <= len; i++) {
        if (line[i] == ' ' || line[i] == '\0') {
            reverse(line, start, i - 1);
            start = i + 1;
        }
    }
    puts(line);
    return 0;
}

原理:"Hello World" → "dlroW olleH" → "World Hello"。單次掃描,原地修改,空間最優,無編譯器依賴,推薦作為標準解法。

五、 方案對比總結

方案空間時間可移植性代碼複雜度推薦場景
雙數組存儲O(N)O(N)★★★★★低教學、通用工具、輸入格式複雜
scanf+EOFO(N)O(N)★★☆☆☆ (依賴文件EOF)極低OJ 刷題、快速驗證、輸入保證單行文件結束
原地反轉O(1)O(N)★★★★★中嵌入式、大數據流、面試標準答案

核心收穫:scanf("%s") + EOF 是 OJ 刷題的「速成技巧」,但不可用於交互式程序或跨平台產品代碼。工程實踐中,請使用 fgets 讀整行 + strtok/手動解析,或原地反轉算法。理解 EOF 在文件流 vs 終端流的行為差異,是 C 語言 IO 系統編程的必修課。

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