34 C 语言字符串转数值函数详解:strtol、strtoll、strtoul、strtoull(含 errno 处理、ERANGE 错误)

发布于:2025-06-08 ⋅ 阅读:(16) ⋅ 点赞:(0)

1 strtol() 函数

1.1 函数原型

#include <stdlib.h> // 必须包含这个头文件才能使用 strtol()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 LONG_MAX 和 LONG_MIN

long int strtol(const char *nptr, char **endptr, int base);

1.2 功能说明

        strtol() 函数用于将字符串转换为长整数(long int)。与 atoi() 和 atol() 等函数相比,strtol() 提供了更灵活、安全的转换机制,支持多种进制以及完善的错误检查。

  • 参数:
    • nptr:指向待转换的字符串(以空字符 \0 结尾)
    • endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL
    • base:进制基数(取值范围为 2 到 36)
      • 若 base 为 0,则自动推断进制
        • 以 0x 或 0X 开头 → 十六进制
        • 以 0 开头(且不是 0x 或 0X)→ 八进制
        • 其他情况 → 十进制
  • 返回值:
    • 成功转换:返回对应的 long int 值
    • 无效输入:若未执行任何转换,返回 0
    • 溢出情况:若转换结果超出 long int 范围
      • 正溢出:返回 LONG_MAX,并设置 errno ERANGE
      • 负溢出:返回 LONG_MIN,并设置 errno ERANGE

错误处理与范围检查相关概念:

  1. errno
    • 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码
    • 作用:当 strtol() 等函数检测到错误(如数值溢出)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型
    • 使用建议:在调用 strtol() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误
  2. ERANGE
    • 含义:ERANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)
    • 使用场景:当 strtol() 检测到输入的字符串表示的数值超出 long int 的表示范围(过大或过小)时,会将 errno 设置为 ERANGE,表明发生了溢出错误
  3. LONG_MAX 和 LONG_MIN
    • 含义:LONG_MAX 和 LONG_MIN 是宏定义(定义在 <limits.h> 头文件中),分别表示 long int 类型的最大值和最小值
    • 使用场景:若输入字符串表示的数值超出 long int 的范围(正数超过 LONG_MAX 或负数低于 LONG_MIN),strtol() 会返回对应边界值(LONG_MAX 或 LONG_MIN)并设置 errno 为 ERANGE

1.3 转换规则

  1. 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等
  2. 识别正负号:支持在数字前添加可选的符号字符
    1. '+' 表示最终数值为正数
    2. '-' 表示最终数值为负数
    3. 若未指定符号,默认数值为正数
  3. 读取数字:根据指定的进制(base,范围为 2 到 36)解析数字字符
    1. 若 base 为 0,则自动推断进制
      1. 以 0x 或 0X 开头 → 十六进制
      2. 以 0 开头(且不是 0x 或 0X)→ 八进制
      3. 其他情况 → 十进制
  4. 停止转换:遇到第一个不符合当前进制规则的字符时停止解析,后续字符将被忽略
  5. 返回结果:将解析的数字转换为 long int 类型返回
  6. 错误处理:
    1. 完全成功转换:
      1. endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证
      2. 示例:"123456789", NULL, 10 → 返回 123456789。
    2. 部分成功转换:
      1. endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证
      2. 示例:"123456789abc", NULL, 10 → 返回 123456789,endptr 指向 'a'。
    3. 无效输入:
      1. endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)
      2. 示例:"HelloWorld", NULL, 10 → 返回 0,endptr 指向 'H'。
    4. 溢出处理:需结合 errno 和 endptr 进行判断
      1. 正溢出:
        1. 返回 LONG_MAX,并设置 errno 为 ERANGE
        2. 示例:"2147483648", NULL, 10 → 返回 LONG_MAX。
      2. 负溢出:
        1. 返回 LONG_MIN,并设置 errno 为 ERANGE
        2. 示例:"-2147483649", NULL, 10 → 返回 LONG_MIN。

        转换示例:

输入字符串 endptr 参数 base 返回值 说明
"123456789" NULL 10 123456789 完全成功转换
"-987654321" NULL 10 -987654321 完全成功转换(负数)
"    +123456789" NULL 10 123456789 忽略前导空格
"123456789abc" NULL 10 123456789 部分成功转换,endptr 指向 'a'
"HelloWorld" NULL 10 0 无效输入,endptr 指向 'H'
"1010" NULL 2 10 二进制转换
"20" NULL 8 16 八进制转换
"FF" NULL 16 255 十六进制转换
"2147483648" NULL 10 LONG_MAX 正溢出
"-2147483649" NULL 10 LONG_MIN 负溢出

1.4 注意事项

  1. 错误检测:
    1. 方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败(即无效输入)
    2. 细节:
      1. 若 endptr == str:表示未解析出任何有效数字(如输入为 "Hello")
      2. 若 *endptr != '\0':表示部分成功转换(如输入为 "123abc",endptr 指向 'a')
      3. 若 *endptr == '\0':表示完全成功转换(如输入为 "123")
  2. 溢出处理:
    1. 条件:当输入的数值超出 long int 的表示范围时,strtol() 会设置 errno 为 ERANGE
    2. 行为:
      1. 正溢出:返回 LONG_MAX
      2. 负溢出:返回 LONG_MIN
    3. 建议:在调用 strtol() 前,应清除 errno(如 errno = 0),以便后续检测
  3. 灵活进制:支持 2 到 36 之间的任意进制转换,若 base 为 0,则根据字符串前缀自动推断进制
  4. 推荐使用:相比 atoi() 系列函数,strtol() 更安全,支持错误检查和进制转换

1.5 示例程序

#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtol()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 LONG_MAX 和 LONG_MIN

int main()
{
    char *endptr;    // 用于存储转换结束的位置
    long int result; // 用于存储转换结果

    // 示例 1:基本转换
    result = strtol("123456789", &endptr, 10);
    printf("转换结果1: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 123456789, 结束位置: (null)

    // 示例 2:带符号的数字
    result = strtol("-987654321", &endptr, 10);
    printf("转换结果2: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果2: -987654321, 结束位置: (null)

    // 示例 3:带前导空格和符号
    result = strtol("    +123456789", &endptr, 10);
    printf("转换结果3: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 123456789, 结束位置: (null)

    // 示例 4:部分有效的数字
    result = strtol("123456789abc", &endptr, 10);
    printf("转换结果4: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 123456789, 结束位置: abc

    // 示例 5:无效输入
    result = strtol("HelloWorld", &endptr, 10);
    printf("转换结果5: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0, 结束位置: HelloWorld

    // 示例 6:二进制转换
    result = strtol("1010", &endptr, 2);
    // 二进制 1010 转换为十进制是 1*2^3 + 0*2^2 + 1*2^1 + 0*2^0 = 8 + 0 + 2 + 0 = 10
    printf("转换结果6: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 10, 结束位置: (null)

    // 示例 7:八进制转换
    result = strtol("20", &endptr, 8);
    // 八进制 20 转换为十进制是 2*8^1 + 0*8^0 = 16 + 0 = 16
    printf("转换结果7: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果7: 16, 结束位置: (null)

    // 示例 8:十六进制转换
    result = strtol("FF", &endptr, 16);
    // 十六进制 FF 转换为十进制是 15*16^1 + 15*16^0 = 240 + 15 = 255
    printf("转换结果8: %ld, 结束位置: %s\n", result, endptr); // 输出: 转换结果8: 255, 结束位置: (null)

    // 示例 9:正溢出检测
    errno = 0;                                  // 重置 errno, 清除之前的错误
    result = strtol("2147483648", &endptr, 10); // 超出 long int 范围(向上溢出)
    // 判断是否超出范围
    if (errno == ERANGE) // ERANGE 是一个宏,表示超出范围
    {
        // 查看 errno 与 ERANGE 的值
        printf("  errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
        // 查看 long int 的最大值
        printf("  LONG_MAX = %ld\n", LONG_MAX); // 输出: LONG_MAX = 2147483647
        // 查看转换结果
        printf("转换结果9: 超出范围,返回 result(LONG_MAX): %ld\n", result);
        // 转换结果9: 超出范围,返回 result(LONG_MAX): 2147483647
    }
    else
    {
        printf("转换结果9: %ld\n", result);
    }

    // 示例 10:负溢出检测
    errno = 0;                                   // 重置 errno, 清除之前的错误
    result = strtol("-2147483649", &endptr, 10); // 超出 long int 范围(向下溢出)
    // 判断是否超出范围
    if (errno == ERANGE) // ERANGE 是一个宏,表示超出范围
    {
        // 查看 errno 与 ERANGE 的值
        printf("  errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
        // 查看 long int 的最小值
        printf("  LONG_MIN = %ld\n", LONG_MIN); // 输出: LONG_MIN = -2147483648
        // 查看转换结果
        printf("转换结果10: 超出范围,返回 result(LONG_MIN): %ld\n", result);
        // 转换结果10: 超出范围,返回 result(LONG_MIN): -2147483648
    }
    else
    {
        printf("转换结果10: %ld\n", result);
    }

    // 示例 11:检查转换是否完全成功
    result = strtol("123abc", &endptr, 10);
    // 检查是否还有未转换的字符
    if (*endptr != '\0')
    {
        printf("转换结果11: %ld, 但字符串未完全转换\n", result); // 输出: 转换结果11: 123, 但字符串未完全转换
    }
    else
    {
        printf("转换结果11: %ld\n", result);
    }

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:


2 strtoll() 函数

2.1 函数原型

#include <stdlib.h> // 必须包含这个头文件才能使用 strtoll()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 LLONG_MAX 和 LLONG_MIN

long long int strtoll(const char *nptr, char **endptr, int base);

2.2 功能说明

        strtoll() 函数用于将字符串转换为长长整数(long long int)。与 strtol() 类似,但支持更大范围的整数(long long int),并提供灵活的进制转换和错误检查机制。

  • 参数:
    • nptr:指向待转换的字符串(以空字符 \0 结尾)
    • endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL
    • base:进制基数(取值范围为 2 到 36)
      • 若 base 为 0,则自动推断进制
        • 以 0x 或 0X 开头 → 十六进制
        • 以 0 开头(且不是 0x 或 0X)→ 八进制
        • 其他情况 → 十进制
  • 返回值:
    • 成功转换:返回对应的 long long int 值
    • 无效输入:若未执行任何转换,返回 0
    • 溢出情况:若转换结果超出 long long int 范围
      • 正溢出:返回 LLONG_MAX,并设置 errno ERANGE
      • 负溢出:返回 LLONG_MIN,并设置 errno ERANGE

错误处理与范围检查相关概念:

  1. errno
    • 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码
    • 作用:当 strtoll() 等函数检测到错误(如数值溢出)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型
    • 使用建议:在调用 strtoll() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误
  2. ERANGE
    • 含义:ERANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)
    • 使用场景:当 strtoll() 检测到输入的字符串表示的数值超出 long long int 的表示范围(过大或过小)时,会将 errno 设置为 ERANGE,表明发生了溢出错误
  3. LLONG_MAX 和 LLONG_MIN
    • 含义:LLONG_MAX 和 LLONG_MIN 是宏定义(定义在 <limits.h> 头文件中),分别表示 long long int 类型的最大值和最小值
    • 使用场景:若输入字符串表示的数值超出 long long int 的范围(正数超过 LLONG_MAX 或负数低于 LLONG_MIN),strtoll() 会返回对应边界值(LLONG_MAX 或 LLONG_MIN)并设置 errno 为 ERANGE

2.3 转换规则

  1. 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等
  2. 识别正负号:支持在数字前添加可选的符号字符
    1. '+' 表示最终数值为正数
    2. '-' 表示最终数值为负数
    3. 若未指定符号,默认数值为正数
  3. 读取数字:根据指定的进制(base,范围为 2 到 36)解析数字字符
    1. 若 base 为 0,则自动推断进制
      1. 以 0x 或 0X 开头 → 十六进制
      2. 以 0 开头(且不是 0x 或 0X)→ 八进制
      3. 其他情况 → 十进制
  4. 停止转换:遇到第一个不符合当前进制规则的字符时停止解析,后续字符将被忽略
  5. 返回结果:将解析的数字转换为 long long int 类型返回
  6. 错误处理:
    1. 完全成功转换:
      1. endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证
      2. 示例:"123456789", NULL, 10 → 返回 123456789。
    2. 部分成功转换:
      1. endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证
      2. 示例:"123456789abc", NULL, 10 → 返回 123456789,endptr 指向 'a'。
    3. 无效输入:
      1. endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)
      2. 示例:"HelloWorld", NULL, 10 → 返回 0,endptr 指向 'H'。
    4. 溢出处理:需结合 errno 和 endptr 进行判断
      1. 正溢出:
        1. 返回 LLONG_MAX,并设置 errno 为 ERANGE
        2. 示例:"9223372036854775808", NULL, 10 → 返回 LLONG_MAX。
      2. 负溢出:
        1. 返回 LLONG_MIN,并设置 errno 为 ERANGE
        2. 示例:"-9223372036854775809", NULL, 10 → 返回 LLONG_MIN。

        转换示例:

输入字符串 endptr 参数 base 返回值 说明
"123456789" NULL 10 123456789 完全成功转换
"-987654321" NULL 10 -987654321 完全成功转换(负数)
"    +123456789" NULL 10 123456789 忽略前导空格
"123456789abc" NULL 10 123456789 部分成功转换,endptr 指向 'a'
"HelloWorld" NULL 10 0 无效输入,endptr 指向 'H'
"1010" NULL 2 10 二进制转换
"20" NULL 8 16 八进制转换
"FF" NULL 16 255 十六进制转换
"9223372036854775808" NULL 10 LLONG_MAX 正溢出
"-9223372036854775809" NULL 10 LLONG_MIN 负溢出

2.4 注意事项

  1. 错误检测:
    1. 方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败(即无效输入)
    2. 细节:
      1. 若 endptr == str:表示未解析出任何有效数字(如输入为 "Hello")
      2. 若 *endptr != '\0':表示部分成功转换(如输入为 "123abc",endptr 指向 'a')
      3. 若 *endptr == '\0':表示完全成功转换(如输入为 "123")
  2. 溢出处理:
    1. ​​​​​​​条件:当输入的数值超出 long long int 的表示范围时,strtoll() 会设置 errno 为 ERANGE
    2. 行为:
      1. 正溢出:返回 LLONG_MAX
      2. 负溢出:返回 LLONG_MIN
    3. 建议:在调用 strtoll() 前,应清除 errno(如 errno = 0),以便后续检测
  3. 灵活进制:支持 2 到 36 之间的任意进制转换,若 base 为 0,则根据字符串前缀自动推断进制
  4. 推荐使用:相比 atoll() 系列函数,strtoll() 更安全,支持错误检查和进制转换

2.5 示例程序

#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtoll()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 LLONG_MAX 和 LLONG_MIN

int main()
{
    char *endptr;     // 用于存储转换结束的位置
    long long result; // 用于存储转换结果

    // 示例 1:基本转换
    result = strtoll("123456789", &endptr, 10);
    printf("转换结果1: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 123456789, 结束位置: (null)

    // 示例 2:带符号的数字
    result = strtoll("-987654321", &endptr, 10);
    printf("转换结果2: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果2: -987654321, 结束位置: (null)

    // 示例 3:带前导空格和符号
    result = strtoll("    +123456789", &endptr, 10);
    printf("转换结果3: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 123456789, 结束位置: (null)

    // 示例 4:部分有效的数字
    result = strtoll("123456789abc", &endptr, 10);
    printf("转换结果4: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 123456789, 结束位置: abc

    // 示例 5:无效输入
    result = strtoll("HelloWorld", &endptr, 10);
    printf("转换结果5: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0, 结束位置: HelloWorld

    // 示例 6:二进制转换
    result = strtoll("1010", &endptr, 2);
    // 二进制 1010 转换为十进制是 1*2^3 + 0*2^2 + 1*2^1 + 0*2^0 = 8 + 0 + 2 + 0 = 10
    printf("转换结果6: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 10, 结束位置: (null)

    // 示例 7:八进制转换
    result = strtoll("20", &endptr, 8);
    // 八进制 20 转换为十进制是 2*8^1 + 0*8^0 = 16 + 0 = 16
    printf("转换结果7: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果7: 16, 结束位置: (null)

    // 示例 8:十六进制转换
    result = strtoll("FF", &endptr, 16);
    // 十六进制 FF 转换为十进制是 15*16^1 + 15*16^0 = 240 + 15 = 255
    printf("转换结果8: %lld, 结束位置: %s\n", result, endptr); // 输出: 转换结果8: 255, 结束位置: (null)

    // 示例 9:正溢出检测
    errno = 0;                                            // 重置 errno, 清除之前的错误
    result = strtoll("9223372036854775808", &endptr, 10); // 超出 long long int 范围(向上溢出)
    // 判断是否超出范围
    if (errno == ERANGE) // ERANGE 是一个宏,表示超出范围
    {
        // 查看 errno 与 ERANGE 的值
        printf("  errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
        // 查看 long long int 的最大值
        printf("  LLONG_MAX = %lld\n", LLONG_MAX); // 输出: LLONG_MAX = 9223372036854775807
        // 查看转换结果
        printf("转换结果9: 超出范围,返回 result(LLONG_MAX): %lld\n", result);
        // 转换结果9: 超出范围,返回 result(LLONG_MAX): 9223372036854775807
    }
    else
    {
        printf("转换结果9: %lld\n", result);
    }

    // 示例 10:负溢出检测
    errno = 0;                                             // 重置 errno, 清除之前的错误
    result = strtoll("-9223372036854775809", &endptr, 10); // 超出 long long int 范围(向下溢出)
    // 判断是否超出范围
    if (errno == ERANGE) // ERANGE 是一个宏,表示超出范围
    {
        // 查看 errno 与 ERANGE 的值
        printf("  errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
        // 查看 long long int 的最小值
        printf("  LLONG_MIN = %lld\n", LLONG_MIN); // 输出: LLONG_MIN = -9223372036854775808
        // 查看转换结果
        printf("转换结果10: 超出范围,返回 result(LLONG_MIN): %lld\n", result);
        // 转换结果10: 超出范围,返回 result(LLONG_MIN): -9223372036854775808
    }
    else
    {
        printf("转换结果10: %lld\n", result);
    }

    // 示例 11:检查转换是否完全成功
    result = strtoll("123abc", &endptr, 10);
    // 检查是否还有未转换的字符
    if (*endptr != '\0')
    {
        printf("转换结果11: %lld, 但字符串未完全转换\n", result); // 输出: 转换结果11: 123, 但字符串未完全转换
    }
    else
    {
        printf("转换结果11: %lld\n", result);
    }

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:


3 strtoul() 函数

3.1 函数原型

#include <stdlib.h> // 必须包含这个头文件才能使用 strtoul()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 ULONG_MAX

unsigned long int strtoul(const char *nptr, char **endptr, int base);

3.2 功能说明

        strtoul() 函数用于将字符串转换为无符号长整数(unsigned long int)。与 strtol() 类似,但支持无符号整数类型,并提供灵活的进制转换和错误检查机制。

  • 参数:
    • nptr:指向待转换的字符串(以空字符 \0 结尾)
    • endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL
    • base:进制基数(取值范围为 2 到 36)
      • 若 base 为 0,则自动推断进制
        • 以 0x 或 0X 开头 → 十六进制
        • 以 0 开头(且不是 0x 或 0X)→ 八进制
        • 其他情况 → 十进制
  • 返回值:
    • 成功转换:返回对应的 unsigned long int 值
    • 无效输入:若未执行任何转换,返回 0
    • 溢出情况:若转换结果超出 unsigned long int 范围,返回 ULONG_MAX,并设置 errno ERANGE

错误处理与范围检查相关概念:

  1. errno
    • 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码
    • 作用:当 strtoul() 等函数检测到错误(如数值溢出)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型
    • 使用建议:在调用 strtoul() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误
  2. ERANGE
    • 含义:ERANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)
    • 使用场景:当 strtoul() 检测到输入的字符串表示的数值超出 unsigned long int 的表示范围(过大)时,会将 errno 设置为 ERANGE,表明发生了溢出错误
  3. ULONG_MAX
    • 含义:ULONG_MAX 是一个宏定义(定义在 <limits.h> 头文件中),表示 unsigned long int 类型的最大值
    • 使用场景:若输入字符串表示的数值超出 unsigned long int 的范围(正数超过 ULONG_MAX),strtoul() 会返回 ULONG_MAX 并设置 errno 为 ERANGE

3.3 转换规则

  1. 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等
  2. 识别正负号:
    1. 正号('+'):
      1. ​​​​​​​​​​​​​​strtoul 允许在数字前添加正号('+'),但无符号整数(unsigned long)无法表示负数,因此正号仅作为语法形式,对结果无实际影响
    2. 负号('-'):
      1. ​​​​​​​​​​​​​​strtoul 不支持负数(范围为 0 到 ULONG_MAX)
      2. 标准行为:停止解析,endptr 指向负号位置('-'),返回 0
      3. 非标准行为:某些实现可能尝试解析负号后的数字,返回未定义值(可能是 0 或补码对应的随机值)
      4. 结论:不要依赖 strtoul 解析负数,否则会导致未定义行为
  3. 读取数字:根据指定的进制(base,范围为 2 到 36)解析数字字符
    1. 若 base 为 0,则自动推断进制
      1. 以 0x 或 0X 开头 → 十六进制
      2. 以 0 开头(且不是 0x 或 0X)→ 八进制
      3. 其他情况 → 十进制
  4. 停止转换:遇到第一个不符合当前进制规则的字符时停止解析,后续字符将被忽略
  5. 返回结果:将解析的数字转换为 unsigned long int 类型返回
  6. 错误处理:
    1. 完全成功转换:
      1. endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证
      2. 示例:"123456789", NULL, 10 → 返回 123456789。
    2. 部分成功转换:
      1. endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证
      2. 示例:"123456789abc", NULL, 10 → 返回 123456789,endptr 指向 'a'。
    3. 无效输入:
      1. endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)
      2. 示例:"HelloWorld", NULL, 10 → 返回 0,endptr 指向 'H'。
    4. 溢出处理:需结合 errno 和 endptr 进行判断
      1. 正溢出:
        1. 返回 ULONG_MAX,并设置 errno 为 ERANGE
        2. 示例:"4294967296", NULL, 10 → 返回 ULONG_MAX。

        转换示例:

输入字符串 endptr 参数 base 返回值 说明
"123456789" NULL 10 123456789 完全成功转换
"-987654321" NULL 10

返回 0(标准行为)或未定义值(取决于实现)

"    +123456789" NULL 10 123456789 忽略前导空格后完全成功转换(正号无实际意义)
"123456789abc" NULL 10 123456789 部分成功转换,endptr 指向 'a'
"HelloWorld" NULL 10 0 无效输入,endptr 指向 'H'
"1010" NULL 2 10 二进制转换
"20" NULL 8 16 八进制转换
"FF" NULL 16 255 十六进制转换
"4294967296" NULL 10 ULONG_MAX 正溢出

3.4 注意事项

  1. 错误检测:
    1. ​​​​​​​​​​​​​​方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败(即无效输入)
    2. 细节:
      1. 若 endptr == str:表示未解析出任何有效数字(如输入为 "Hello")
      2. 若 *endptr != '\0':表示部分成功转换(如输入为 "123abc",endptr 指向 'a')
      3. 若 *endptr == '\0':表示完全成功转换(如输入为 "123")
  2. 溢出处理:
    1. ​​​​​​​​​​​​​​条件:当输入的数值超出 unsigned long int 的表示范围时,strtoul() 会设置 errno 为 ERANGE
    2. 行为:
      1. 正溢出:返回 ULONG_MAX
    3. 建议:在调用 strtoul() 前,应清除 errno(如 errno = 0),以便后续检测
  3. 灵活进制:支持 2 到 36 之间的任意进制转换,若 base 为 0,则根据字符串前缀自动推断进制
  4. 推荐使用:相比 atol() 系列函数,strtoul() 更安全,支持错误检查和进制转换

3.5 示例程序

#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtoul()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 ULONG_MAX

int main()
{
    char *endptr;         // 用于存储转换结束的位置
    unsigned long result; // 用于存储转换结果

    // 示例 1:基本转换
    result = strtoul("123456789", &endptr, 10);
    printf("转换结果1: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 123456789, 结束位置: (null)

    // 示例 2:带符号的数字
    result = strtoul("-987654321", &endptr, 10);
    printf("转换结果2: %lu, 结束位置: %s\n", result, endptr); // 输出: 0(标准行为)或未定义值(取决于实现)

    // 示例 3:带前导空格和符号(正号无实际意义)
    result = strtoul("    +123456789", &endptr, 10);
    printf("转换结果3: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 123456789, 结束位置: (null)

    // 示例 4:部分有效的数字
    result = strtoul("123456789abc", &endptr, 10);
    printf("转换结果4: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 123456789, 结束位置: abc

    // 示例 5:无效输入
    result = strtoul("HelloWorld", &endptr, 10);
    printf("转换结果5: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0, 结束位置: HelloWorld

    // 示例 6:二进制转换
    result = strtoul("1010", &endptr, 2);
    // 二进制 1010 转换为十进制是 1*2^3 + 0*2^2 + 1*2^1 + 0*2^0 = 8 + 0 + 2 + 0 = 10
    printf("转换结果6: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 10, 结束位置: (null)

    // 示例 7:八进制转换
    result = strtoul("20", &endptr, 8);
    // 八进制 20 转换为十进制是 2*8^1 + 0*8^0 = 16 + 0 = 16
    printf("转换结果7: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果7: 16, 结束位置: (null)

    // 示例 8:十六进制转换
    result = strtoul("FF", &endptr, 16);
    // 十六进制 FF 转换为十进制是 15*16^1 + 15*16^0 = 240 + 15 = 255
    printf("转换结果8: %lu, 结束位置: %s\n", result, endptr); // 输出: 转换结果8: 255, 结束位置: (null)

    // 示例 9:正溢出检测
    errno = 0;                                   // 重置 errno, 清除之前的错误
    result = strtoul("4294967296", &endptr, 10); // 超出 unsigned long int 范围(向上溢出)
    // 判断是否超出范围
    if (errno == ERANGE) // ERANGE 是一个宏,表示超出范围
    {
        // 查看 errno 与 ERANGE 的值
        printf("  errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
        // 查看 unsigned long int 的最大值
        printf("  ULONG_MAX = %lu\n", ULONG_MAX); // 输出: ULONG_MAX = 4294967295
        // 查看转换结果
        printf("转换结果9: 超出范围,返回 result(ULONG_MAX): %lu\n", result);
        // 转换结果9: 超出范围,返回 result(ULONG_MAX): 4294967295
    }
    else
    {
        printf("转换结果9: %lu\n", result);
    }

    // 示例 10:检查转换是否完全成功
    result = strtoul("123abc", &endptr, 10);
    // 检查是否还有未转换的字符
    if (*endptr != '\0')
    {
        printf("转换结果10: %lu, 但字符串未完全转换\n", result); // 输出: 转换结果10: 123, 但字符串未完全转换
    }
    else
    {
        printf("转换结果10: %lu\n", result);
    }

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:


4 strtoull() 函数

4.1 函数原型

#include <stdlib.h> // 必须包含这个头文件才能使用 strtoull()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 ULLONG_MAX

unsigned long long int strtoull(const char *nptr, char **endptr, int base);

4.2 功能说明

        strtoull() 函数用于将字符串转换为无符号长长整数(unsigned long long int)。与 strtoul() 类似,但支持更大的无符号整数类型,并提供灵活的进制转换和错误检查机制。​​​​​​​

  • 参数:
    • nptr:指向待转换的字符串(以空字符 \0 结尾)
    • endptr:用于存储转换结束位置的指针。如果不需要使用该信息,可以将其设置为 NULL
    • base:进制基数(取值范围为 2 到 36)
      • 若 base 为 0,则自动推断进制
        • 以 0x 或 0X 开头 → 十六进制
        • 以 0 开头(且不是 0x 或 0X)→ 八进制
        • 其他情况 → 十进制
  • 返回值:
    • 成功转换:返回对应的 unsigned long long int 值
    • 无效输入:若未执行任何转换,返回 0
    • 溢出情况:若转换结果超出 unsigned long long int 范围,返回 ULLONG_MAX,并设置 errno ERANGE

错误处理与范围检查相关概念:

  1. errno
    • 含义:errno 是一个全局变量(类型为 int,定义在 <errno.h> 头文件中),用于存储最近一次函数调用产生的错误代码
    • 作用:当 strtoull() 等函数检测到错误(如数值溢出)时,会将 errno 设置为特定的错误码(如 ERANGE),以指示错误类型
    • 使用建议:在调用 strtoull() 之前,应清除 errno(例如,通过 errno = 0),以便在转换后能够正确检测到是否发生了错误
  2. ERANGE
    • 含义:ERANGE 是一个宏常量(通常值为 34,定义在 <errno.h> 头文件中),表示 “结果超出范围”(Range Error)
    • 使用场景:当 strtoull() 检测到输入的字符串表示的数值超出 unsigned long long int 的表示范围(正数超过 ULLONG_MAX)时,会将 errno 设置为 ERANGE,表明发生了溢出错误
  3. ULLONG_MAX
    • 含义:ULLONG_MAX 是一个宏定义(定义在 <limits.h> 头文件中),表示 unsigned long long int 类型的最大值
    • 使用场景:若输入字符串表示的数值超出 unsigned long long int 的范围(正数超过 ULLONG_MAX),strtoull() 会返回 ULLONG_MAX 并设置 errno 为 ERANGE

4.3 转换规则

  1. 忽略前导空格:在解析字符串时,自动跳过字符串开头处的所有空白字符,包括空格(' ')、制表符('\t')、换行符('\n')等
  2. 识别正负号:
    1. 正号('+'):
      1. ​​​​​​​strtoull 允许在数字前添加正号('+'),但无符号整数(unsigned long long)无法表示负数,因此正号仅作为语法形式,对结果无实际影响
    2. 负号('-'):
      1. ​​​​​​​​​​​​​​strtoull 不支持负数(范围为 0 到 ULLONG_MAX)
      2. 标准行为:停止解析,endptr 指向负号位置('-'),返回 0
      3. 非标准行为:某些实现可能尝试解析负号后的数字,返回未定义值(可能是 0 或补码对应的随机值)
      4. 结论:不要依赖 strtoull 解析负数,否则会导致未定义行为
  3. 读取数字:根据指定的进制(base,范围为 2 到 36)解析数字字符
    1. 若 base 为 0,则自动推断进制
      1. 以 0x 或 0X 开头 → 十六进制
      2. 以 0 开头(且不是 0x 或 0X)→ 八进制
      3. 其他情况 → 十进制
  4. 停止转换:遇到第一个不符合当前进制规则的字符时停止解析,后续字符将被忽略
  5. 返回结果:将解析的数字转换为 unsigned long long int 类型返回
  6. 错误处理:
    1. 完全成功转换:
      1. endptr 指向字符串末尾的空字符 '\0',可通过 if (*endptr == '\0') 验证
      2. 示例:"12345678901234567890", NULL, 10 → 返回 12345678901234567890。
    2. 部分成功转换:
      1. endptr 指向第一个非数字字符,可通过 if (*endptr != '\0') 验证
      2. 示例:"12345678901234567890abc", NULL, 10 → 返回 12345678901234567890,endptr 指向 'a'。
    3. 无效输入:
      1. endptr 指向原始字符串起始地址,可通过 if (endptr == str) 验证(str 为原始字符串指针)
      2. 示例:"HelloWorld", NULL, 10 → 返回 0,endptr 指向 'H'。
    4. 溢出处理:需结合 errno 和 endptr 进行判断
      1. 正溢出:
        1. 返回 ULLONG_MAX,并设置 errno 为 ERANGE
        2. 示例:"18446744073709551616", NULL, 10 → 返回 ULLONG_MAX。

        转换示例:

输入字符串 endptr 参数 base 返回值 说明
"12345678901234567890" NULL 10 12345678901234567890 完全成功转换
"-987654321" NULL 10

返回 0(标准行为)或未定义值(取决于实现)

"    +12345678901234567890" NULL 10 12345678901234567890 忽略前导空格后完全成功转换(正号无实际意义)
"12345678901234567890abc" NULL 10 12345678901234567890 部分成功转换,endptr 指向 'a'
"HelloWorld" NULL 10 0 无效输入,endptr 指向 'H'
"1010" NULL 2 10 二进制转换
"20" NULL 8 16 八进制转换
"FF" NULL 16 255 十六进制转换
"18446744073709551616" NULL 10 ULLONG_MAX 正溢出

4.4 注意事项

  1. 错误检测:
    1. ​​​​​​​​​​​​​​方法:通过检查 endptr 是否等于原始字符串的起始地址(str)来判断转换是否完全失败(即无效输入)
    2. 细节:
      1. 若 endptr == str:表示未解析出任何有效数字(如输入为 "Hello")
      2. 若 *endptr != '\0':表示部分成功转换(如输入为 "123abc",endptr 指向 'a')
      3. 若 *endptr == '\0':表示完全成功转换(如输入为 "123")
  2. 溢出处理:
    1. ​​​​​​​​​​​​​​条件:当输入的数值超出 unsigned long long int 的表示范围时,strtoull() 会设置 errno 为 ERANGE
    2. 行为:
      1. 正溢出:返回 ULLONG_MAX
    3. 建议:在调用 strtoull() 前,应清除 errno(如 errno = 0),以便后续检测
  3. 灵活进制:支持 2 到 36 之间的任意进制转换,若 base 为 0,则根据字符串前缀自动推断进制
  4. 推荐使用:相比 atoll() 系列函数,strtoull() 更安全,支持错误检查和进制转换

4.5 示例程序

#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 strtoull()
#include <errno.h>  // 包含 errno 和 ERANGE
#include <limits.h> // 包含 ULLONG_MAX

int main()
{
    char *endptr;              // 用于存储转换结束的位置
    unsigned long long result; // 用于存储转换结果

    // 示例 1:基本转换
    result = strtoull("12345678901234567890", &endptr, 10);
    printf("转换结果1: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果1: 12345678901234567890, 结束位置: (null)

    // 示例 2:带符号的数字
    result = strtoull("-987654321", &endptr, 10);
    printf("转换结果2: %llu, 结束位置: %s\n", result, endptr); // 输出: 0(标准行为)或未定义值(取决于实现)

    // 示例 3:带前导空格和符号(正号无实际意义)
    result = strtoull("    +12345678901234567890", &endptr, 10);
    printf("转换结果3: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果3: 12345678901234567890, 结束位置: (null)

    // 示例 4:部分有效的数字
    result = strtoull("12345678901234567890abc", &endptr, 10);
    printf("转换结果4: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果4: 12345678901234567890, 结束位置: abc

    // 示例 5:无效输入
    result = strtoull("HelloWorld", &endptr, 10);
    printf("转换结果5: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果5: 0, 结束位置: HelloWorld

    // 示例 6:二进制转换
    result = strtoull("1010", &endptr, 2);
    // 二进制 1010 转换为十进制是 1*2^3 + 0*2^2 + 1*2^1 + 0*2^0 = 8 + 0 + 2 + 0 = 10
    printf("转换结果6: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果6: 10, 结束位置: (null)

    // 示例 7:八进制转换
    result = strtoull("20", &endptr, 8);
    // 八进制 20 转换为十进制是 2*8^1 + 0*8^0 = 16 + 0 = 16
    printf("转换结果7: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果7: 16, 结束位置: (null)

    // 示例 8:十六进制转换
    result = strtoull("FF", &endptr, 16);
    // 十六进制 FF 转换为十进制是 15*16^1 + 15*16^0 = 240 + 15 = 255
    printf("转换结果8: %llu, 结束位置: %s\n", result, endptr); // 输出: 转换结果8: 255, 结束位置: (null)

    // 示例 9:正溢出检测
    errno = 0;                                              // 重置 errno, 清除之前的错误
    result = strtoull("18446744073709551616", &endptr, 10); // 超出 unsigned long long int 范围(向上溢出)
    // 判断是否超出范围
    if (errno == ERANGE) // ERANGE 是一个宏,表示超出范围
    {
        // 查看 errno 与 ERANGE 的值
        printf("  errno = %d, ERANGE = %d\n", errno, ERANGE); // 输出: errno = 34, ERANGE = 34
        // 查看 unsigned long long int 的最大值
        printf("  ULLONG_MAX = %llu\n", ULLONG_MAX); // 输出: ULLONG_MAX = 18446744073709551615
        // 查看转换结果
        printf("转换结果9: 超出范围,返回 result(ULLONG_MAX): %llu\n", result);
        // 转换结果9: 超出范围,返回 result(ULLONG_MAX): 18446744073709551615
    }
    else
    {
        printf("转换结果9: %llu\n", result);
    }

    // 示例 10:检查转换是否完全成功
    result = strtoull("123abc", &endptr, 10);
    // 检查是否还有未转换的字符
    if (*endptr != '\0')
    {
        printf("转换结果10: %llu, 但字符串未完全转换\n", result); // 输出: 转换结果10: 123, 但字符串未完全转换
    }
    else
    {
        printf("转换结果10: %llu\n", result);
    }

    return 0;
}

        程序在 VS Code 中的运行结果如下所示:


5 字符串转数值函数总结

函数名 功能 返回值类型 转换范围 适用场景 错误处理(errno)
strtol 将字符串转换为有符号长整型(long) long LONG_MIN 到 LONG_MAX 需要处理正负数的有符号整数转换 无效输入时返回 0,溢出时返回 LONG_MIN 或 LONG_MAX,并设置 errno 为 ERANGE
strtoll 将字符串转换为有符号长长整型(long long) long long LLONG_MIN 到 LLONG_MAX 需要处理大范围有符号整数转换 无效输入时返回 0,溢出时返回 LLONG_MIN 或 LLONG_MAX,并设置 errno 为 ERANGE
strtoul 将字符串转换为无符号长整型(unsigned long) unsigned long 0 到 ULONG_MAX 需要处理非负数的无符号整数转换 无效输入时返回 0,溢出时返回 ULONG_MAX,并设置 errno 为 ERANGE
strtoull 将字符串转换为无符号长长整型(unsigned long long) unsigned long long 0 到 ULLONG_MAX 需要处理大范围非负数的无符号转换 无效输入时返回 0,溢出时返回 ULLONG_MAX,并设置 errno 为 ERANGE
  • 头文件:所有这些函数都定义在 <stdlib.h> 头文件中,使用时需要包含该头文件。
  • 错误处理细节:
    • 当转换的字符串不以有效的数字字符开头时,函数将返回 0,并且如果 endptr 参数不为 NULL,则 endptr 将指向字符串的起始位置。这表明函数未能解析出任何有效的数值
    • 如果转换结果超出目标类型的范围(即大于 LONG_MAX 或小于 LONG_MIN,或大于 LLONG_MAX 或小于 LLONG_MIN),函数将返回该类型的最大值或最小值(具体取决于溢出的方向),并设置 errno 为 ERANGE
    • 如果转换结果超出目标类型的范围(即大于 ULONG_MAX 或 ULLONG_MAX),函数将返回该类型的最大值(ULONG_MAX 或 ULLONG_MAX),并设置 errno 为 ERANGE
  • 跨平台兼容性:
    • 这些函数是 C 标准库的一部分,因此在大多数平台上都可用。
    • 然而,不同平台上的 long 和 long long 的大小可能不同,因此在跨平台开发时需要注意数值范围的变化。

网站公告

今日签到

点亮在社区的每一天
去签到