字符串与数字转换:atoi、atof、sprintf、sscanf 的用法,手写 itoa 函数
字符串和数字之间的转换,是C语言里最基础也最常用的操作之一。我做了这么多年嵌入式开发,几乎每个项目都离不开这几个函数。今天咱们就把它们彻底讲透。
一、为什么需要转换?
你想想看,我们写程序时,数据在内存里是以二进制存的。但用户看到的、文件里存的、串口发出来的,都是字符串。比如你从键盘读了个"123",它其实是三个字符'1'、'2'、'3',不是整数123。反过来,你想把计算结果打印到屏幕上,也得把数字变成字符串。
说白了,转换就是数据在「人眼可读的形式」和「机器可算的形式」之间来回倒腾。
二、atoi:字符串转整数
atoi 全称是 ASCII to Integer。原型很简单:
#include <stdlib.h>
int atoi(const char *str);
它把字符串开头的数字部分解析出来,遇到非数字字符就停。比如:
char buf[] = " -42abc";
int val = atoi(buf); // val = -42
嗯,这里要注意:atoi 会跳过开头的空白字符(空格、制表符、换行),然后处理可选的正负号,接着读连续的数字。
strtol 替代,它能提供更详细的错误信息。
我曾经在一个数据采集项目里,因为没检查 atoi 的返回值,结果传感器坏了输出乱码,程序一直当0处理,查了两天才发现。从那以后,我对这类函数都格外小心。
三、atof:字符串转浮点数
atof 是 ASCII to Float,用法和 atoi 类似:
#include <stdlib.h>
double atof(const char *str);
它支持科学计数法,比如 "3.14e2" 会被解析成 314.0。
char buf[] = " 3.14159";
double pi = atof(buf); // pi = 3.14159
strtod。
四、sprintf:数字转字符串
sprintf 是格式化输出的利器。它把各种类型的数据按指定格式写入字符串缓冲区。
#include <stdio.h>
int sprintf(char *buf, const char *format, ...);
看个例子:
char buf[64];
int num = 255;
sprintf(buf, "十进制:%d,十六进制:%x,八进制:%o", num, num, num);
// buf = "十进制:255,十六进制:ff,八进制:377"
snprintf,它能指定最大写入长度。
snprintf(buf, sizeof(buf), "数值:%d", num); // 安全写法
五、sscanf:字符串解析利器
sscanf 是 scanf 的字符串版本,从字符串里按格式提取数据。
#include <stdio.h>
int sscanf(const char *str, const char *format, ...);
比如解析一个日期字符串:
char date[] = "2024-03-15";
int year, month, day;
sscanf(date, "%d-%d-%d", &year, &month, &day);
// year=2024, month=3, day=15
我记得有一次解析GPS数据,字符串格式偶尔会乱,没检查返回值就直接用,结果程序跑着跑着就崩了。加上返回值检查后,问题立刻定位到了。
六、手写 itoa 函数
标准C库没有 itoa(整数转字符串),但很多平台自己实现了。咱们自己写一个,加深理解。
思路很简单:不断取模得到最低位,然后除以10去掉最低位,直到数字变成0。注意处理负数和0的特殊情况。
// 手写 itoa:整数转字符串(十进制)
void my_itoa(int num, char *str) {
int i = 0;
int is_negative = 0;
// 处理0的特殊情况
if (num == 0) {
str[i++] = '0';
str[i] = '\0';
return;
}
// 处理负数
if (num < 0) {
is_negative = 1;
num = -num; // 注意:INT_MIN 取反会溢出!
}
// 逐位取出数字(结果是逆序的)
while (num > 0) {
str[i++] = (num % 10) + '0';
num /= 10;
}
// 添加负号
if (is_negative) {
str[i++] = '-';
}
str[i] = '\0';
// 反转字符串
int start = 0;
int end = i - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
改进版:
void my_itoa_safe(int num, char *str) {
// 用 unsigned 避免溢出问题
unsigned int unum;
int i = 0;
int is_negative = 0;
if (num == 0) {
str[i++] = '0';
str[i] = '\0';
return;
}
if (num < 0) {
is_negative = 1;
unum = (unsigned int)(-(num + 1)) + 1; // 安全取反
} else {
unum = (unsigned int)num;
}
while (unum > 0) {
str[i++] = (unum % 10) + '0';
unum /= 10;
}
if (is_negative) {
str[i++] = '-';
}
str[i] = '\0';
// 反转
int start = 0, end = i - 1;
while (start < end) {
char t = str[start];
str[start] = str[end];
str[end] = t;
start++;
end--;
}
}
七、知识体系总览
下面这张图帮你理清这几个函数的关系和使用场景:
八、总结与避坑
这几个函数,说白了就是C语言里最常用的「翻译官」。我给你的建议是:
- 能用 snprintf 就别用 sprintf —— 安全第一
- 能用 strtol/strtod 就别用 atoi/atof —— 错误处理更完善
- sscanf 一定要检查返回值 —— 我吃过这个亏
- 手写 itoa 时注意 INT_MIN 的溢出 —— 用 unsigned 绕过去
好了,字符串和数字转换这块就讲到这里。代码写多了你会发现,这些基础函数用好了,能省不少调试时间。