首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >字符串转整数函数详解:atoi、atol、strtol 与 strtoimax

字符串转整数函数详解:atoi、atol、strtol 与 strtoimax

作者头像
byte轻骑兵
发布2026-01-20 17:41:07
发布2026-01-20 17:41:07
1670
举报

在 C/C++ 开发中,字符串与整数的转换是高频需求,比如解析配置文件、处理用户输入、网络数据解析等场景。C 标准库提供了atoi、atol、strtol和strtoimax四类核心转换函数,它们各有特性与适用场景。


一、函数整体简介

字符串转整数函数的核心目标是将ASCII 编码的数字字符串(如"123"、"-456")转换为对应整数类型,但因设计定位不同,四类函数在转换范围、功能灵活性上差异显著,先通过下表建立整体认知:

函数名

核心功能

转换目标类型

关键特性

适用场景

atoi

字符串转整数

int

仅支持 10 进制,无溢出检测

简单 int 范围转换(如配置值)

atol

字符串转长整数

long

仅支持 10 进制,无溢出检测

long 范围转换(如时间戳)

strtol

字符串转长整数(灵活版)

long

支持多进制(2-36),可检测溢出

需定制进制 / 错误处理场景

strtoimax

字符串转最大宽度整数

intmax_t

支持多进制,跨平台大整数兼容

跨平台大整数转换(如 64 位)

注:intmax_t是 C99 标准定义的 “最大宽度整数类型”,能容纳当前平台所有整数类型的最大值,确保跨平台兼容性(如 32 位平台为long long,64 位平台为long)。

二、函数原型与参数解析

所有函数均声明在<stdlib.h>头文件中(strtoimax需额外包含<inttypes.h>),原型与参数含义如下,需重点关注 “输入输出参数”(如endptr)的用法:

1. atoi 原型

代码语言:javascript
复制
int atoi(const char *str);
  • 参数:str - 待转换的字符串(需以数字 / 正负号开头,后续可跟非数字字符);
  • 返回值:转换后的int值;若无法转换(如全非数字),返回 0。

2. atol 原型

代码语言:javascript
复制
long atol(const char *str);
  • 参数:同atoi,仅转换目标类型不同;
  • 返回值:转换后的long值;无法转换时返回 0。

3. strtol 原型(核心函数)

代码语言:javascript
复制
long strtol(const char *str, char **endptr, int base);

核心参数解析

  • str:待转换字符串;
  • endptr:输出参数(非 NULL 时),指向 “转换终止的字符位置”(如"123abc"转换后,*endptr指向'a');
  • base:转换进制,取值范围2~36或0:
    • 当base=0时,自动识别进制:以0x开头→16 进制,以0开头→8 进制,否则→10 进制;
    • 当2≤base≤36时,仅识别对应进制的字符(如base=16时,a-f/A-F为有效字符)。

返回值:转换后的long值;溢出时返回LONG_MAX/LONG_MIN(需结合errno判断);无法转换时返回 0。

4. strtoimax 原型

代码语言:javascript
复制
intmax_t strtoimax(const char *str, char **endptr, int base);
  • 参数:完全同strtol;
  • 返回值:转换后的intmax_t值;溢出时返回INTMAX_MAX/INTMAX_MIN,无法转换时返回 0。

三、函数实现原理

字符串转整数的核心逻辑是 “字符→数字映射 + 累加计算”,但需处理空白字符、正负号、进制规则、溢出检测四大关键场景。以下伪代码还原四类函数的核心实现(基于 C 标准逻辑):

1. atoi 伪代码(简化版)

代码语言:javascript
复制
function atoi(str):
    // 步骤1:跳过空白字符(空格、制表符'\t'、换行符'\n'等)
    while str指向的字符是空白字符:
        str = str + 1  // 指针后移
    
    // 步骤2:处理正负号
    sign = 1  // 默认为正
    if str指向的字符是'-':
        sign = -1
        str = str + 1
    else if str指向的字符是'+':
        str = str + 1
    
    // 步骤3:转换数字字符(仅10进制)
    result = 0
    while str指向的字符是'0'~'9':
        digit = 字符的ASCII值 - '0'的ASCII值(即转为数字)
        result = result * 10 + digit  // 累加计算
        str = str + 1
    
    // 步骤4:返回带符号结果(无溢出检测!)
    return result * sign

2. strtol 伪代码(完整功能版)

代码语言:javascript
复制
function strtol(str, endptr, base):
    // 初始化:记录结果、符号、初始指针
    long result = 0
    int sign = 1
    const char *original_str = str  // 保存原始指针
    
    // 步骤1:跳过空白字符
    while isspace(*str):  // isspace()判断空白字符
        str++
    
    // 步骤2:处理正负号
    if (*str == '-'):
        sign = -1
        str++
    elif (*str == '+'):
        str++
    
    // 步骤3:处理进制(base=0时自动识别)
    if (base == 0):
        if (*str == '0'):
            str++
            if (*str == 'x' || *str == 'X'):  // 0x开头→16进制
                base = 16
                str++
            else:  // 仅0开头→8进制
                base = 8
        else:  // 无前缀→10进制
            base = 10
    elif (base < 2 || base > 36):  // 非法进制,直接返回0
        if (endptr != NULL):
            *endptr = original_str  // endptr指向原始位置
        return 0
    
    // 步骤4:转换数字字符(支持2-36进制)
    while True:
        // 字符转数字:0-9→0-9,a-z/A-Z→10-35
        if (*str >= '0' && *str <= '9'):
            digit = *str - '0'
        elif (*str >= 'a' && *str <= 'z'):
            digit = *str - 'a' + 10
        elif (*str >= 'A' && *str <= 'Z'):
            digit = *str - 'A' + 10
        else:
            break  // 非有效字符,终止转换
        
        // 关键:溢出检测(避免result*base + digit超出long范围)
        if (sign == 1):
            if (result > LONG_MAX / base || (result == LONG_MAX / base && digit > LONG_MAX % base)):
                errno = ERANGE  // 设置溢出错误码
                return LONG_MAX
        else:
            if (result > LONG_MIN / (-base) || (result == LONG_MIN / (-base) && digit > -(LONG_MIN % base))):
                errno = ERANGE
                return LONG_MIN
        
        // 累加计算
        result = result * base + digit
        str++
    
    // 步骤5:设置endptr(若非NULL)
    if (endptr != NULL):
        *endptr = (char *)str  // 指向转换终止的字符
    
    // 步骤6:返回带符号结果
    return result * sign

3. atol 与 strtoimax 伪代码逻辑

  • atol:仅将atoi的int替换为long,无其他差异;
  • strtoimax:将strtol的long替换为intmax_t,溢出判断改为INTMAX_MAX/INTMAX_MIN,其余逻辑完全一致。

四、使用场景:按需选择函数

选择函数的核心依据是转换范围、进制需求、错误处理精度,以下是典型场景与对应函数的匹配:

1. atoi:简单 int 范围转换

  • 适用场景:明确输入在int范围内(如-2147483648~2147483647),且仅需 10 进制转换;
  • 示例:解析配置文件中 “端口号”(如"8080")、“重试次数”(如"3")等小数值。

2. atol:long 范围转换

  • 适用场景:输入超出int范围但在long范围内(如 32 位平台long为 4 字节,64 位平台为 8 字节);
  • 示例:解析 Unix 时间戳(如"1699999999",值约为 2023 年,需long存储)。

3. strtol:灵活转换与错误处理

  • 适用场景

①需非 10 进制转换(如二进制"1010"、16 进制"0x1A");

②需判断转换是否完全(如"123abc"是否仅转换前 3 个字符);

③需检测溢出(如输入"9999999999"是否超出long范围);

  • 示例:解析硬件寄存器值(通常为 16 进制字符串,如"0xFF23")、用户输入的进制切换场景(如 “请输入二进制数字”)。

4. strtoimax:跨平台大整数转换

  • 适用场景

①需跨平台兼容(如 32 位 / 64 位平台统一处理 64 位整数);

②输入可能超出long范围(如 64 位整数"9223372036854775807");

  • 示例:解析日志中的大数值 ID(如用户 ID、订单号)、跨平台数据传输中的整数字符串(如 JSON 中的大数字)。

五、注意事项

四类函数的 “隐藏风险” 主要集中在溢出处理、非数字字符、错误判断上,需牢记以下规则:

1. 溢出问题:atoi/atol 无检测,strtol/strtoimax 需结合 errno

  • atoi/atol 风险:若输入超出int/long范围,结果是 “未定义行为”(可能返回随机值,甚至导致程序崩溃);
  • strtol/strtoimax 正确用法
代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main() {
    const char *str = "9999999999";  // 超出32位long范围
    errno = 0;  // 清零errno
    long num = strtol(str, NULL, 10);
    if (errno == ERANGE) {
        printf("溢出:%s\n", strerror(errno));  // 输出"溢出:Numerical result out of range"
        return 1;
    }
    printf("结果:%ld\n", num);
    return 0;
}

①转换前需将errno清零(errno是全局变量,默认值可能非 0);

②转换后检查errno是否为ERANGE(溢出标志);

2. 非数字字符:仅转换 “前缀有效部分”

所有函数仅转换字符串开头的有效数字,遇到非数字字符立即终止,需注意两种特殊情况:

  • 示例 1:" -123abc456" → 转换结果为-123(跳过空白→处理负号→转换 123→遇到 'a' 终止);
  • 示例 2:"abc123" → 无法转换,返回 0(无有效数字前缀)。

3. endptr 用法:判断转换是否 “完全有效”

strtol/strtoimax的endptr参数可用于检测 “是否所有字符都被转换”,避免误判:

  • 示例:若需确保输入是 “纯数字字符串”(无后续非数字字符),可检查*endptr是否为字符串结束符'\0':
代码语言:javascript
复制
const char *str = "123abc";
char *end;
long num = strtol(str, &end, 10);
if (*end != '\0') {
    printf("存在非数字字符:%s\n", end);  // 输出"存在非数字字符:abc"
}

4. 进制参数 base 的合法范围

strtol/strtoimax的base仅支持0或2~36,若传入 1 或 37 等非法值,函数直接返回 0,且endptr指向原始字符串(无转换)。

六、示例代码:实战演练

以下通过 4 个场景示例,覆盖四类函数的典型用法,代码可直接编译运行(需支持 C99 标准,编译命令gcc -std=c99 文件名.c)。

示例 1:atoi 解析配置端口号

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

// 解析配置中的端口号(需在1~65535范围内)
int parse_port(const char *port_str) {
    int port = atoi(port_str);
    // 二次校验:避免atoi返回0(可能是无效输入或合法0)
    if (port <= 0 || port > 65535) {
        printf("无效端口号:%s\n", port_str);
        return -1;
    }
    return port;
}

int main() {
    const char *config_port = "8080";  // 配置文件中的端口字符串
    int port = parse_port(config_port);
    if (port != -1) {
        printf("解析成功,端口:%d\n", port);  // 输出"解析成功,端口:8080"
    }
    return 0;
}

示例 2:atol 解析 Unix 时间戳

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 解析时间戳字符串为本地时间
void parse_timestamp(const char *ts_str) {
    long ts = atol(ts_str);
    struct tm *local_time = localtime(&ts);  // 转换为本地时间结构
    if (local_time == NULL) {
        printf("无效时间戳:%s\n", ts_str);
        return;
    }
    // 格式化输出时间(年-月-日 时:分:秒)
    printf("时间:%d-%02d-%02d %02d:%02d:%02d\n",
           local_time->tm_year + 1900,  // tm_year是从1900年开始的差值
           local_time->tm_mon + 1,      // tm_mon是0-11
           local_time->tm_mday,
           local_time->tm_hour,
           local_time->tm_min,
           local_time->tm_sec);
}

int main() {
    const char *ts = "1699999999";  // 对应2023-11-14 10:53:19
    parse_timestamp(ts);
    return 0;
}

示例 3:strtol 解析 16 进制寄存器值

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

// 解析16进制寄存器值(如"0xFF23"),返回long类型
long parse_reg(const char *reg_str) {
    char *end;
    errno = 0;
    // base=16:强制16进制转换,支持0x前缀或无前缀
    long reg_val = strtol(reg_str, &end, 16);
    
    // 错误判断:溢出或无有效转换
    if (errno == ERANGE) {
        printf("溢出:%s\n", strerror(errno));
        return -1;
    }
    if (end == reg_str) {  // 未转换任何字符
        printf("无效16进制字符串:%s\n", reg_str);
        return -1;
    }
    // 可选:检查是否有多余非数字字符
    if (*end != '\0') {
        printf("警告:存在多余字符:%s\n", end);
    }
    return reg_val;
}

int main() {
    const char *reg1 = "0xFF23";  // 带0x前缀
    const char *reg2 = "1A3F";    // 无前缀
    printf("reg1值:%ld(十进制)\n", parse_reg(reg1));  // 输出"reg1值:65315(十进制)"
    printf("reg2值:%ld(十进制)\n", parse_reg(reg2));  // 输出"reg2值:6719(十进制)"
    return 0;
}

示例 4:strtoimax 跨平台解析大整数

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h>  // strtoimax所需头文件

// 解析大整数字符串(如64位ID)
intmax_t parse_big_id(const char *id_str) {
    char *end;
    errno = 0;
    intmax_t id = strtoimax(id_str, &end, 10);
    
    if (errno == ERANGE) {
        printf("大整数溢出:%s\n", strerror(errno));
        return -1;
    }
    if (end == id_str) {
        printf("无效大整数:%s\n", id_str);
        return -1;
    }
    return id;
}

int main() {
    // 64位最大整数(9223372036854775807)
    const char *big_id = "9223372036854775807";
    intmax_t id = parse_big_id(big_id);
    if (id != -1) {
        // PRIdMAX是intmax_t的格式化宏,确保跨平台输出正确
        printf("大整数ID:%" PRIdMAX "\n", id);  // 输出"大整数ID:9223372036854775807"
    }
    return 0;
}

七、函数差异对比

为方便快速查阅,将四类函数的核心差异整理为表格,覆盖 “类型、功能、错误处理” 等关键维度:

对比维度

atoi

atol

strtol

strtoimax

转换目标类型

int

long

long

intmax_t(最大宽度)

支持进制

仅 10 进制

仅 10 进制

2~36 或 0(自动识别)

2~36 或 0(自动识别)

溢出处理

未定义行为(无检测)

未定义行为(无检测)

支持(返回极值 +errno=ERANGE)

支持(返回极值 +errno=ERANGE)

转换终止位置获取

不支持

不支持

支持(endptr参数)

支持(endptr参数)

跨平台大整数兼容

不支持

部分支持(依赖 long 宽度)

部分支持(依赖 long 宽度)

完全支持(C99 标准)

无法转换时返回值

0

0

0

0

头文件

<stdlib.h>

<stdlib.h>

<stdlib.h>

<stdlib.h>+<inttypes.h>

八、经典面试题

面试题 1:请说明atoi函数的主要缺陷,并给出改进思路。

参考答案

atoi的核心缺陷有 3 点:

  1. 无溢出检测:输入超出int范围时,结果是未定义行为(如 32 位平台输入"2147483648",可能返回-2147483648);
  2. 无法区分 “合法 0” 与 “转换失败”:若输入是"0"或"abc",均返回 0,无法判断是否为有效转换;
  3. 功能单一:仅支持 10 进制,无自定义进制或转换终止位置获取功能。

改进思路(参考strtol设计):

  • 增加溢出检测:计算过程中判断result * 10 + digit是否超出int范围,溢出时设置错误标志;
  • 增加endptr参数:通过输出指针指向转换终止位置,区分 “全非数字”(endptr指向原始字符串)与 “合法 0”(endptr指向'0'后);
  • 支持多进制:增加base参数,支持 2-36 进制转换。

面试题 2:strtol函数的endptr参数有什么作用?如何用它判断字符串是否 “完全转换”?

参考答案

endptr是输出参数(需传入非 NULL 指针),其作用是 “指向字符串中转换终止的字符位置”—— 即函数遇到第一个非有效数字字符时,*endptr会指向该字符。

判断 “完全转换”(字符串全为有效数字)的逻辑:

转换后检查*endptr是否等于字符串结束符'\0'。若等于,则所有字符均被转换;若不等于,则存在非数字字符。

示例代码片段:

代码语言:javascript
复制
const char *str = "1234";
char *end;
strtol(str, &end, 10);
if (*end == '\0') {
    printf("完全转换:所有字符均为有效数字\n");
} else {
    printf("未完全转换:终止于字符 '%c'\n", *end);
}

面试题 3:为什么说strtoimax是 “跨平台大整数转换的首选函数”?它与strtol的核心区别是什么?

参考答案

strtoimax成为跨平台首选的原因:

strtoimax的转换目标类型是intmax_t(C99 标准定义的 “最大宽度整数类型”),该类型能容纳当前平台所有整数类型的最大值 —— 无论平台是 32 位还是 64 位,intmax_t都能确保存储最大整数,避免因long宽度差异(32 位平台long为 4 字节,64 位为 8 字节)导致的兼容性问题。

与strtol的核心区别:

  1. 转换目标类型不同:strtol目标是long,strtoimax目标是intmax_t;
  2. 跨平台兼容性不同:strtol依赖long的宽度,32 位平台无法存储 64 位整数;strtoimax基于intmax_t,跨平台无需担心宽度差异;
  3. 头文件不同:strtol仅需<stdlib.h>,strtoimax需额外包含<inttypes.h>(定义intmax_t和格式化宏)。

博主简介 byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发,深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域。乐于技术分享与交流,欢迎关注互动! 📌 主页与联系方式

  • CSDN:https://blog.csdn.net/weixin_37800531
  • 知乎:https://www.zhihu.com/people/38-72-36-20-51
  • 微信公众号:嵌入式硬核研究所
  • 邮箱:byteqqb@163.com(技术咨询或合作请备注需求)

⚠️ 版权声明 本文为原创内容,未经授权禁止转载。商业合作或内容授权请联系邮箱并备注来意。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、函数整体简介
  • 二、函数原型与参数解析
  • 三、函数实现原理
  • 四、使用场景:按需选择函数
  • 五、注意事项
  • 六、示例代码:实战演练
  • 七、函数差异对比
  • 八、经典面试题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档