字串反轉是陣列操作基礎訓練,本文實作讀取多行文字並逐行反轉輸出的 C 程式,深入解析輸入緩衝區處理、字串長度計算、原地反轉與輸出反轉兩種策略差異。
完整程式碼實作
#include <stdio.h>
#include <string.h>
#define MAX_LINE 1000
int main() {
int n;
char line[MAX_LINE];
scanf("%d", &n);
getchar(); // 吸收換行符
while (n--) {
if (fgets(line, sizeof(line), stdin) == NULL) break;
// 移除 fgets 保留的換行符
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') {
line[len-1] = '\0';
len--;
}
// 反序輸出
for (size_t i = len; i-- > 0; ) {
putchar(line[i]);
}
if (n > 0) putchar('\n');
}
return 0;
}
關鍵技術細節
輸入緩衝區處理
scanf("%d", &n) 留下換行符在緩衝區
getchar() 單獨吸收該換行符
fgets() 讀取整行含換行符,需手動移除
迴圈邊界寫法比較
// 寫法 1:size_t 無號型別陷阱
for (size_t i = len; i > 0; i--) // 錯誤:i=0 時 i-- 變巨大值
// 寫法 2:正確遞減 (C99)
for (size_t i = len; i-- > 0; )
// 寫法 3:使用有號整數
for (int i = (int)len - 1; i >= 0; i--)
// 寫法 4:指標算術
for (char *p = line + len - 1; p >= line; p--)
原地反轉 vs 輸出反轉
| 策略 | 程式碼 | 優點 | 缺點 |
| 輸出反轉 | 直接逆序 putchar | 無額外記憶體、保留原字串 | 無法後續處理反轉結果 |
| 原地反轉 | 雙指標交換 | 結果可重複使用 | 修改原資料、需可寫緩衝區 |
原地反轉實作
void reverse_inplace(char *str) {
char *left = str;
char *right = str + strlen(str) - 1;
while (left < right) {
char tmp = *left;
*left++ = *right;
*right-- = tmp;
}
}
進階功能擴充
單字反轉 (保持單字順序)
void reverse_words(char *str) {
char *start = str;
while (*str) {
if (*str == ' ') {
reverse_range(start, str - 1);
start = str + 1;
}
str++;
}
reverse_range(start, str - 1); // 最後一個單字
reverse_range(str - strlen(str), str - 1); // 整行反轉
}
Unicode/多位元組字元支援
ASCII 字串反轉直接對位元組操作即可。UTF-8 需解碼為 code point 陣列再反轉,否則會破壞多位元組字元結構。
大檔案行反轉
// 記憶體映射大檔案反轉
#include <sys/mman.h>
#include <fcntl.h>
void reverse_file_lines(const char *filename) {
int fd = open(filename, O_RDWR);
struct stat st; fstat(fd, &st);
char *data = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 找出所有行邊界,兩兩交換...
}
常見錯誤與最佳實踐
- 緩衝區溢位:固定
MAX_LINE 可能不足,動態分配或 getline() (POSIX) 更安全
- 空行處理:
len=0 時迴圈不執行,直接輸出空行正確
- 換行符殘留:
fgets 保留 \n,必須移除否則反轉會跑到行首
- Windows CRLF:讀取
\r\n 需同時移除 \r
標準庫替代方案
// C++ 標準庫一行解決
#include <algorithm>
#include <string>
std::reverse(str.begin(), str.end());
// C strrev (非標準,MSVC 提供)
char *strrev(char *str); // 原地反轉並回傳指標
效能分析
- 時間複雜度:O(n) 每行長度
- 空間複雜度:O(1) 輸出反轉 / O(n) 原地反轉
- 快取友好:循序存取,分支預測簡單
- 向量化潛力:編譯器可自動 SIMD 化原地交換迴圈
字串反轉雖屬基礎操作,但涵蓋緩衝區管理、指標算術、無號整數陷阱、編碼認知等關鍵 C 語言核心概念。掌握多種實作策略與邊界處理,是建立紮實系統程式設計功力的重要一環。
- 上一篇文章: C語言計算第 N 個閏年:日期運算與閏年判斷完整實作
- 下一篇文章: C語言實作:單向循環鏈表建立與操作完整教學(含圖解邏輯)
Copyright ©
程式碼之家 All Rights Reserved