字符串與數值互轉是 C 程式最高頻操作之一。標準庫提供了 atof/atoi/atol 簡易族與 strtod/strtol/strtoul 健壯族、gcvt/ecvt/fcvt 浮點轉字符串、toascii/tolower/toupper 字符映射等函數。本文逐一解析原型、行為差異、錯誤處理並給出可編譯實例。
| 函數 | 原型 | 返回 | 缺陷 |
|---|---|---|---|
atof | double atof(const char *nptr) | double | 無錯誤報告,溢出行為未定義 |
atoi | int atoi(const char *nptr) | int | 同上,等價 strtol(nptr,NULL,10) |
atol | long atol(const char *nptr) | long | 同上,等價 strtol(nptr,NULL,10) |
建議:僅用於快速原型/教學,**生產代碼請改用 strto* 族**。
strto*(首選)double strtod(const char *nptr, char **endptr); long strtol(const char *nptr, char **endptr, int base); unsigned long strtoul(const char *nptr, char **endptr, int base);
endptr 指向第一個無效字符,可精確判斷解析範圍。base=0 時自動識別 0x(16進制)、0(8進制)、其餘(10進制)。errno=ERANGE 並返回 HUGE_VAL/LONG_MAX/ULONG_MAX。#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
long safe_strtol(const char *s, int base, int *ok) {
char *end;
errno = 0;
long val = strtol(s, &end, base);
if (errno == ERANGE || end == s || *end != '\0') {
*ok = 0;
return 0;
}
*ok = 1;
return val;
}
int main(void) {
int ok;
printf("%ld\n", safe_strtol("123", 10, &ok)); // 123, ok=1
printf("%ld\n", safe_strtol("0xFF", 0, &ok)); // 255, ok=1
printf("%ld\n", safe_strtol("9999999999999", 10, &ok)); // 0, ok=0 (溢出)
return 0;
}
gcvt/ecvt/fcvt)| 函數 | 特點 | 備註 |
|---|---|---|
gcvt | 結果含小數點/符號,四捨五入到指定有效位數 | 緩衝區由調用者分配 |
ecvt | 科學計數法,無小數點,返回靜態緩衝區指針 | 非線程安全 |
fcvt | 定點格式,無小數點,返回靜態緩衝區指針 | 非線程安全 |
現代替代:snprintf(buf, sizeof(buf), "%.*g", prec, val) 或 C++ std::to_chars (C++17)。
int toascii(int c):c & 0x7F,將任意值壓縮為 7-bit ASCII。int tolower(int c) / toupper(int c):僅轉換字母,其他字符原樣返回。需包含 <ctype.h>。EOF 或負值時行為未定義,**必須轉為 unsigned char 後傳入**:tolower((unsigned char)ch)。/* 解析 "3.14 + 2" 形式的簡單表達式 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
int main(void) {
char line[256];
while (fgets(line, sizeof(line), stdin)) {
char *p = line;
while (isspace((unsigned char)*p)) ++p;
int ok1, ok2;
double a = strtod(p, &p);
if (!ok1) continue;
while (isspace((unsigned char)*p)) ++p;
char op = *p++;
while (isspace((unsigned char)*p)) ++p;
double b = strtod(p, &p);
if (!ok2) continue;
double res = (op=='+')?a+b:(op=='-')?a-b:(op=='*')?a*b:(op=='/')?a/b:0;
printf("= %g\n", res);
}
return 0;
}