itoa (Integer to ASCII) 將整數轉為字串表示。雖非標準 C 庫函數(標準用 sprintf 或 snprintf),但手寫實作是理解「位權分解」、「遞迴展開順序」、「字串構建」的經典練習。本文對比遞迴版、迭代版、負數處理、緩衝區安全、基數擴展實作。
#include <stdio.h>
static int idx = 0; // 全域索引 (教學用,非執行緒安全)
void itoa_recursive(int n, char s[]) {
if (n < 0) { // 處理負數
s[idx++] = '-';
n = -n; // 注意 INT_MIN 溢位
}
if (n / 10) { // 遞迴處理高位
itoa_recursive(n / 10, s);
}
s[idx++] = (n % 10) + '0'; // 當前位轉字符
s[idx] = '\0'; // 結束符
}
int main() {
char s[20];
int num = -12345;
idx = 0;
itoa_recursive(num, s);
printf("%s\n", s); // -12345
return 0;
}
itoa(-12345)
→ s[0]='-', n=12345
→ itoa(1234)
→ itoa(123)
→ itoa(12)
→ itoa(1)
→ n/10=0, 不遞迴
→ s[1]='1'
→ s[2]='2'
→ s[3]='3'
→ s[4]='4'
→ s[5]='5'
→ s[6]='\0'
結果: "-12345"
#include <stdio.h>
#include <string.h>
void itoa_iterative(int n, char s[]) {
int i = 0;
bool negative = false;
if (n == 0) { s[i++] = '0'; s[i] = '\0'; return; }
if (n < 0) {
negative = true;
n = -n; // INT_MIN 仍有溢位風險
}
// 從低位到高位存入
while (n > 0) {
s[i++] = (n % 10) + '0';
n /= 10;
}
if (negative) s[i++] = '-';
s[i] = '\0';
// 反轉字串 (低位在前 → 高位在前)
for (int left = 0, right = i - 1; left < right; left++, right--) {
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
}
}
#include <limits.h>
#include <stdbool.h>
// 回傳寫入字元數 (不含 \0),負值表示錯誤
int itoa_safe(int n, char *buf, size_t bufSize) {
if (!buf || bufSize == 0) return -1;
// 特殊處理 INT_MIN:-2147483648
if (n == INT_MIN) {
const char *minStr = "-2147483648";
size_t len = strlen(minStr);
if (bufSize <= len) return -2; // 緩衝區太小
strcpy(buf, minStr);
return len;
}
int i = 0;
bool neg = false;
if (n < 0) { neg = true; n = -n; }
// 計算所需位數 (含符號)
int temp = n;
int digits = 1;
while (temp >= 10) { temp /= 10; digits++; }
if (neg) digits++;
if (bufSize <= digits) return -2; // 空間不足
// 寫入數字 (低位在前)
do {
buf[i++] = (n % 10) + '0';
n /= 10;
} while (n > 0);
if (neg) buf[i++] = '-';
buf[i] = '\0';
// 反轉
for (int l = 0, r = i - 1; l < r; l++, r--) {
char t = buf[l]; buf[l] = buf[r]; buf[r] = t;
}
return i;
}
const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void itoa_base(int n, char *buf, int base) {
if (base < 2 || base > 36) { *buf = '\0'; return; }
int i = 0;
bool neg = false;
if (n < 0 && base == 10) { neg = true; n = -n; }
do {
buf[i++] = digits[n % base];
n /= base;
} while (n > 0);
if (neg) buf[i++] = '-';
buf[i] = '\0';
// 反轉
for (int l = 0, r = i - 1; l < r; l++, r--) {
char t = buf[l]; buf[l] = buf[r]; buf[r] = t;
}
}
| 維度 | 遞迴版 | 迭代版 | 安全版 |
|---|---|---|---|
| 可讀性 | 高 (符合數學定義) | 中 | 低 |
| 執行效率 | 函數呼叫開銷 | 最快 | 略慢 (檢查) |
| 棧空間 | O(log n) | O(1) | O(1) |
| 執行緒安全 | 否 (全域 idx) | 是 | 是 |
| 緩衝區保護 | 無 | 無 | 完整 |
| INT_MIN 處理 | 錯誤 | 錯誤 | 正確 |
| 適用場景 | 教學、面試 | 一般用途 | 庫函數、生產代碼 |
// 推薦:snprintf (C99 標準、緩衝區安全、支援所有型別) char buf[32]; snprintf(buf, sizeof(buf), "%d", -12345); // "-12345" // 任意基數:無標準函數,需手寫或用第三方庫
手寫 itoa 能深入理解「除基取餘得低位、反序輸出得正確順序」的核心邏輯,以及遞迴與迭代的時空權衡。建議實作安全版並納入個人工具庫,同時熟練使用 snprintf 解決實際開發需求。