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

目录

一、函数简介

1.1. atoi (ASCII to Integer)

1.2. atol (ASCII to Long)

1.3. strtol (String to Long)

1.4. strtoimax (String to Integer Maximum)

总结

二、函数原型

2.1. atoi

2.2 atol

2.3. strtol

2.4. strtoimax

三、函数实现(伪代码)

3.1. 简化的 atoi实现概念

3.2. 简化的 atol实现概念

3.3. 简化的 strtol 实现概念

3.4. 简化的 strtoimax 实现概念

四、使用场景

4.1. atoi (字符串转整数)

4.2. atol (字符串转长整数)

4.3. strtol (字符串转长整数,更健壮)

4.4. strtoimax (字符串转最大宽度整数)

总结

五、注意事项 

5.1. 溢出处理

5. 2. 非法输入

5. 3. 基数(仅适用于 strtol 和 strtoimax)

5.4. 字符串结束指针(endptr,仅适用于 strtol 和 strtoimax)

5.5. 局部性和性能

结论

六、使用示例

6.1. atoi 示例

6.2. atol 示例

6.3. strtol 示例 

6.4. strtoimax 示例


一、函数简介

字符串转整数是编程中常见的需求,C语言标准库提供了多个函数来处理这一需求,包括atoiatolstrtolstrtoimax。这些函数各有特点,适用于不同的场景和精度要求。

1.1. atoi (ASCII to Integer)

  • 功能:将字符串str转换为int类型的整数。它扫描字符串直到遇到第一个非数字字符或字符串结束符\0,然后停止转换。
  • 注意atoi不会报告转换过程中遇到的错误(如溢出),并且在无法转换为整数时返回0。此外,它无法处理超过int类型表示范围的数值。

1.2. atol (ASCII to Long)

  • 功能:与atoi类似,但将字符串转换为long int类型的整数。它同样无法报告错误,也不能处理超出long int范围的数值。
  • 注意:与atoi一样,atol也不会在转换失败时提供错误指示。

1.3. strtol (String to Long)

  • 功能:将字符串nptr转换为long int类型的整数,但提供了更多的灵活性和错误处理能力。
  • 注意strtol能够处理范围超出long int的数值,并通过返回值和errno(在需要时)报告溢出。

1.4. strtoimax (String to Integer Maximum)

  • 功能:与strtol类似,但使用intmax_t类型,这是C99标准中引入的能够表示最大带符号整数类型的别名。
  • 注意strtoimax提供了处理最大范围整数的能力,适用于需要高精度整数转换的场景。

总结

  • 选择函数:对于基本的转换需求,如果不需要考虑错误处理和精度,可以使用atoiatol。然而,为了更好的错误处理和更大的灵活性,推荐使用strtolstrtoimax
  • 错误处理atoiatol不提供错误处理,而strtolstrtoimax通过返回值和可选的endptr参数提供了更丰富的错误处理机制。
  • 精度strtolstrtoimax提供了不同的精度选择,strtoimax能够处理更大范围的整数。

二、函数原型

在C语言中,字符串转整数的函数atoiatolstrtolstrtoimax的函数原型分别如下。

2.1. atoi

int atoi(const char *str);
  • 参数const char *str - 指向要转换的字符串的指针。
  • 返回类型int - 转换后的整数。
  • 头文件<stdlib.h>

2.2 atol

long int atol(const char *str);
  • 参数const char *str - 指向要转换的字符串的指针。
  • 返回类型long int - 转换后的整数。
  • 头文件<stdlib.h>

2.3. strtol

long int strtol(const char *nptr, char **endptr, int base);
  • 参数
    • const char *nptr - 指向要转换的字符串的指针。
    • char **endptr - 如果非 NULL,则指向一个 char **,该参数会指向字符串中第一个未被转换的字符的地址。
    • int base - 转换时使用的基数(进制),范围从 2 到 36。如果为 0,则自动根据字符串的格式(如前缀 "0x" 表示十六进制)确定基数。
  • 返回类型long int - 转换后的整数。
  • 头文件<stdlib.h>

2.4. strtoimax

intmax_t strtoimax(const char *nptr, char **endptr, int base);
  • 参数
    • const char *nptr - 指向要转换的字符串的指针。
    • char **endptr - 如果非 NULL,则指向一个 char **,该参数会指向字符串中第一个未被转换的字符的地址。
    • int base - 转换时使用的基数(进制),范围从 2 到 36。如果为 0,则自动根据字符串的格式确定基数。
  • 返回类型intmax_t - 转换后的整数,这是一个能够表示最大带符号整数类型的别名,定义在 <stdint.h> 或 <inttypes.h> 头文件中。
  • 头文件<inttypes.h>(在C99及更高版本中可用)

请注意,虽然 strtol 和 strtoimax 能够处理超出其返回类型实际表示范围的数值,但它们会返回该类型的最大值(LONG_MAX 或 INTMAX_MAX)或最小值(LONG_MIN 或 INTMAX_MIN)来指示溢出,并且(在某些实现中)可能会设置 errno 为 ERANGE 来进一步指示这种情况。然而,并非所有 C 库实现都会为 strtol 和 strtoimax 设置 errno 来指示溢出,这取决于具体的实现。

另外,endptr 参数是一个非常有用的特性,它允许调用者检查转换过程中实际消耗了多少输入字符串,从而可以检测到像前导空格或尾随非数字字符这样的情况。

此外,对于想要编写可移植代码的开发者来说,<stdlib.h> 和 <inttypes.h> 都是标准C库的一部分,但请注意 <inttypes.h> 可能在早于C99的编译器中不可用。

三、函数实现(伪代码)

在C语言标准库中,atoiatolstrtol 和 strtoimax 这些函数的实现细节是依赖于具体实现的,比如 glibc、musl libc、Microsoft C Runtime (MSVCRT) 等。即它们的具体实现可能会因不同的编译器和库而异。然而,我们可以提供一个简化的 strtol 函数的伪代码实现,以帮助理解这些函数的基本工作原理。

请注意,以下示例并不是这些函数的实际实现,而是展示了它们可能如何工作的一个高级概述。

3.1简化的 atoi实现概念

#include <ctype.h>  
#include <limits.h>  
  
int my_atoi(const char *str) {  
    int sign = 1;  // 默认为正数  
    int result = 0;  
  
    // 跳过前导空白符  
    while (isspace((unsigned char)*str)) {  
        str++;  
    }  
  
    // 处理正负号  
    if (*str == '-') {  
        sign = -1;  
        str++;  
    } else if (*str == '+') {  
        str++;  
    }  
  
    // 转换数字  
    while (isdigit((unsigned char)*str)) {  
        // 注意:这里简单地将字符转换为数字并累加到结果上,  
        // 但没有处理整数溢出的情况。在实际实现中,需要添加溢出检查。  
        result = result * 10 + (*str - '0');  
        str++;  
  
        // 如果需要,可以在这里添加溢出检查  
        // if (result > INT_MAX / 10 || (result == INT_MAX / 10 && *str - '0' >= 7 + sign)) {  
        //     // 处理溢出  
        //     // 注意:对于负数,溢出条件略有不同  
        //     errno = ERANGE;  
        //     return sign == 1 ? INT_MAX : INT_MIN;  
        // }  
    }  
  
    // 返回最终结果,考虑符号  
    return sign * result;  
}  
  
// 注意:上面的伪代码没有处理所有可能的错误情况,特别是整数溢出。  
// 在实际实现中,应该添加适当的错误处理和边界检查。

3.2. 简化的 atol实现概念

atol 的实现与 atoi 非常相似,只是返回类型和可能处理的数值范围不同(long 而不是 int)。

#include <ctype.h>  
#include <limits.h>  
  
long int my_atol(const char *str) {  
    int sign = 1;  
    long int result = 0;  
  
    // 跳过前导空白符  
    while (isspace((unsigned char)*str)) {  
        str++;  
    }  
  
    // 处理正负号  
    if (*str == '-') {  
        sign = -1;  
        str++;  
    } else if (*str == '+') {  
        str++;  
    }  
  
    // 转换数字  
    while (isdigit((unsigned char)*str)) {  
        // 注意:这里简单地将字符转换为数字并累加到结果上,  
        // 但没有处理整数溢出的情况。在实际实现中,需要添加溢出检查。  
        result = result * 10 + (*str - '0');  
        str++;  
  
        // 如果需要,可以在这里添加溢出检查  
        // if (result > LONG_MAX / 10 || (result == LONG_MAX / 10 && *str - '0' >= 7 + sign)) {  
        //     // 处理溢出  
        //     errno = ERANGE;  
        //     return sign == 1 ? LONG_MAX : LONG_MIN;  
        // }  
    }  
  
    // 返回最终结果,考虑符号  
    return sign * result;  
}  
  
// 注意:与 atoi 类似,这个伪代码也没有处理所有可能的错误情况,特别是整数溢出。

在上面的伪代码中,我添加了一些注释来指示在哪里可以添加整数溢出的检查。然而,请注意,由于 atol 和 atoi 的原始实现可能不设置 errno(这取决于具体的 C 库实现),因此在实际应用中,可能需要根据需求来决定是否要设置 errno

3.3. 简化的 strtol 实现概念

#include <limits.h>  
#include <errno.h>  
  
long int my_strtol(const char *nptr, char **endptr, int base) {  
    long int result = 0;  
    int sign = 1;  
    int digit;  
  
    // 跳过前导空白符  
    while (isspace((unsigned char)*nptr)) {  
        nptr++;  
    }  
  
    // 处理正负号  
    if (*nptr == '-') {  
        sign = -1;  
        nptr++;  
    } else if (*nptr == '+') {  
        nptr++;  
    }  
  
    // 处理数字  
    while ((digit = get_digit_value(*nptr, base)) >= 0) {  
        if (result > (LONG_MAX - digit) / base) {  
            // 处理溢出  
            errno = ERANGE;  
            if (endptr != NULL) {  
                *endptr = (char *)nptr;  
            }  
            return sign == 1 ? LONG_MAX : LONG_MIN;  
        }  
        result = result * base + digit;  
        nptr++;  
    }  
  
    // 设置endptr(如果非NULL)  
    if (endptr != NULL) {  
        *endptr = (char *)nptr;  
    }  
  
    return sign * result;  
}  
  
// 这是一个假设的函数,用于根据基数获取字符的数字值  
// 注意:这在实际实现中会更复杂,需要处理十六进制等  
int get_digit_value(char c, int base) {  
    // 简化实现,仅处理十进制  
    if (c >= '0' && c <= '9' && c - '0' < base) {  
        return c - '0';  
    }  
    // 对于其他基数,需要添加更多逻辑  
    return -1; // 表示不是有效的数字字符  
}

请注意,上面的 get_digit_value 函数和溢出检测逻辑都是高度简化的。在实际实现中,get_digit_value 需要能够处理不同基数下的字符(如十六进制中的 'a' 到 'f'),并且溢出检测会更加复杂,因为它需要考虑到 intmax_t 的确切大小和范围。

3.4. 简化的 strtoimax 实现概念

strtoimax 的实现与 strtol 非常相似,但使用 intmax_t 而不是 long int 作为结果类型。由于 intmax_t 的大小和范围可能因平台而异,因此处理溢出的方式也需要相应地调整。

#include <inttypes.h>  
#include <errno.h>  
  
intmax_t my_strtoimax(const char *nptr, char **endptr, int base) {  
    intmax_t result = 0;  
    int sign = 1;  
    int digit;  
  
    // 类似于strtol的实现,但使用intmax_t  
    // ...(跳过前导空白符、处理正负号、处理数字)  
  
    // 处理溢出(这里只是一个简化的示例)  
    if (/* 检测到溢出 */) {  
        errno = ERANGE;  
        if (endptr != NULL) {  
            *endptr = (char *)nptr;  
        }  
        return sign == 1 ? INTMAX_MAX : INTMAX_MIN;  
    }  
  
    // ...(设置endptr并返回结果)  
}

请注意,由于 strtol 和 strtoimax 的实际实现可能会使用汇编语言优化、更复杂的错误处理机制以及针对特定硬件和编译器的优化,因此上面的示例仅用于教学目的。

四、使用场景

字符串转整数的函数 atoiatolstrtol 和 strtoimax 在 C 和 C++ 程序中有着广泛的应用场景,每个函数都根据其特性适用于不同的情况。

4.1. atoi (字符串转整数)

  • 使用场景atoi 是最简单的字符串转整数函数,它将字符串转换为 int 类型的值。它适合在不需要复杂错误处理或转换大整数值时快速将字符串转换为整数。然而,由于它不提供转换的结束位置,也不设置 errno 来指示错误(如溢出),因此它在需要详细错误报告的应用中可能不是最佳选择。
  • 示例:解析用户输入或配置文件中的小整数。

4.2. atol (字符串转长整数)

  • 使用场景atol 类似于 atoi,但它将字符串转换为 long 类型的值,适用于需要更大整数范围的情况。同样,它也不提供转换的结束位置或设置 errno 来指示错误。
  • 示例:在处理较大的数字,但仍然不需要详细错误报告时。

4.3. strtol (字符串转长整数,更健壮)

  • 使用场景strtol 是比 atoi 和 atol 更健壮的函数,因为它允许指定基数(如二进制、八进制、十进制或十六进制),并且返回转换的结束位置(通过第二个参数),同时能够在溢出时设置 errno。这使得 strtol 成为处理不确定格式和范围的字符串到整数转换的首选函数。
  • 示例:解析来自多种来源(如文件、网络或用户输入)的、可能包含无效字符或表示不同基数的整数字符串。

4.4. strtoimax (字符串转最大宽度整数)

  • 使用场景strtoimax 是 C99 标准中引入的,用于将字符串转换为 intmax_t 类型的值,intmax_t 是 <stdint.h> 中定义的一种整数类型,它表示该平台上可用的最大宽度的有符号整数类型。这个函数在需要处理尽可能大的整数时非常有用。
  • 示例:在需要确保整数转换不会因平台差异而受到限制的跨平台应用中,特别是在处理大数或进行精确的整数运算时。

总结

  • 选择哪个函数取决于具体需求,包括需要转换的整数的大小、是否需要详细的错误报告(如溢出)、以及是否需要处理不同基数的输入。
  • atoi 和 atol 简单易用,但功能有限。
  • strtol 提供了更多的灵活性和错误处理能力。
  • strtoimax 适用于需要最大整数宽度的场景。

五、注意事项 

在使用字符串转整数的函数 atoiatolstrtol 和 strtoimax 时,有几个重要的注意事项需要考虑,以确保程序的正确性和健壮性。

5.1. 溢出处理

  • atoi 和 atol:这两个函数在发生溢出时不会设置 errno,也不会提供任何指示溢出的直接方式。它们只是简单地返回 INT_MAX 或 INT_MIN(对于 atoi)或 LONG_MAX 或 LONG_MIN(对于 atol),这可能导致数据丢失且难以检测。
  • strtol 和 strtoimax:这些函数在溢出时会设置 errno 为 ERANGE,并返回 LONG_MAXLONG_MIN(对于 strtol)或 INTMAX_MAXINTMAX_MIN(对于 strtoimax),但带有正确的符号。这允许你通过检查 errno 来检测溢出。

5. 2. 非法输入

  • 所有这些函数都会尝试解析字符串中的前导空白字符(如空格、制表符等),并跳过它们。然而,如果字符串不包含任何可转换为整数的字符(即全部由非数字字符组成,除了可选的正负号和一个可选的前导空格),则 atoi 和 atol 会返回 0,而 strtol 和 strtoimax 会返回 0 并设置 endptr(如果提供了该参数)指向字符串的起始位置。
  • 如果字符串包含无法转换为整数的字符(如字母或特殊字符),则 strtol 和 strtoimax 会停止转换,并将 endptr 设置为指向第一个无法转换的字符。这允许你检查哪些部分被成功转换,哪些部分不是。

5. 3. 基数(仅适用于 strtol 和 strtoimax

  • 这两个函数允许你指定一个基数(radix),用于解释字符串中的数字。基数可以是 2 到 36 之间的任何值。如果你不确定字符串的基数,通常可以假设它是 10(十进制)。但是,如果字符串以 "0x" 或 "0X" 开头,许多实现会自动将其视为十六进制数(基数为 16)。

5.4. 字符串结束指针(endptr,仅适用于 strtol 和 strtoimax

  • 这两个函数都接受一个指向 char ** 的参数(通常称为 endptr),该参数在函数返回时被设置为指向字符串中第一个未被转换的字符。这允许你检查哪些部分被成功转换,哪些部分保留原样。

5.5. 局部性和性能

  • 在性能敏感的应用中,atoi 和 atol 可能因为它们的简单性而具有更好的性能。然而,如果你需要处理复杂的输入或需要详细的错误报告,那么 strtol 和 strtoimax 的额外开销可能是值得的。

结论

  • 总是考虑你的具体需求(如整数大小、错误处理、基数等)来选择最合适的函数。
  • 尽量避免使用 atoi 和 atol,除非确定输入是有效的,并且不需要处理溢出或错误情况。
  • 使用 strtol 或 strtoimax 时,请检查 errno 和 endptr 以获取有关转换过程和结果的更多信息。

六、使用示例

下面是一些使用 atoiatolstrtol 和 strtoimax 函数将字符串转换为整数的示例。

6.1. atoi 示例

#include <stdio.h>  
#include <stdlib.h>  
  
int main() {  
    const char *str = "12345";  
    int num = atoi(str);  
    printf("The number is: %d\n", num);  
  
    // 处理非法输入  
    const char *invalidStr = "abc123";  
    int invalidNum = atoi(invalidStr); // 注意:这将返回0,因为'a'不是数字  
    printf("The invalid number is: %d\n", invalidNum);  
  
    return 0;  
}

6.2. atol 示例

#include <stdio.h>  
#include <stdlib.h>  
  
int main() {  
    const char *str = "123456789012345"; // 注意:这个数可能超出了int的范围  
    long num = atol(str);  
    printf("The long number is: %ld\n", num);  
  
    // 处理非法输入  
    const char *invalidStr = "abc123";  
    long invalidNum = atol(invalidStr); // 注意:这将返回0,因为'a'不是数字  
    printf("The invalid long number is: %ld\n", invalidNum);  
  
    return 0;  
}

6.3. strtol 示例 

#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <limits.h>  
  
int main() {  
    const char *str = "12345abc";  
    char *endptr;  
    long num = strtol(str, &endptr, 10); // 基数为10  
  
    if (errno == ERANGE) {  
        // 处理溢出  
        perror("strtol: out of range");  
    } else {  
        printf("The number is: %ld\n", num);  
        if (*endptr != '\0') {  
            // 处理字符串中剩余的非数字字符  
            printf("Remaining string: %s\n", endptr);  
        }  
    }  
  
    // 十六进制转换示例  
    const char *hexStr = "0x1A3F";  
    long hexNum = strtol(hexStr, NULL, 16);  
    printf("The hex number is: %lx\n", hexNum);  
  
    return 0;  
}

6.4. strtoimax 示例

#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <inttypes.h>  
  
int main() {  
    const char *str = "9223372036854775807"; // 接近INTMAX_MAX  
    char *endptr;  
    intmax_t num = strtoimax(str, &endptr, 10);  
  
    if (errno == ERANGE) {  
        // 处理溢出  
        perror("strtoimax: out of range");  
    } else {  
        printf("The intmax_t number is: %" PRIdMAX "\n", num);  
        if (*endptr != '\0') {  
            // 处理字符串中剩余的非数字字符  
            printf("Remaining string: %s\n", endptr);  
        }  
    }  
  
    // 更大的数,导致溢出  
    const char *overflowStr = "9223372036854775808"; // INTMAX_MAX + 1  
    intmax_t overflowNum = strtoimax(overflowStr, NULL, 10);  
    if (errno == ERANGE) {  
        printf("Overflow detected: %" PRIdMAX "\n", overflowNum);  
    }  
  
    return 0;  
}

请注意,在 strtol 和 strtoimax 的示例中,我使用了 errno 来检查是否发生了溢出,并使用了 endptr 来检查字符串中是否还有剩余的非数字字符。此外,PRIdMAX 是一个宏,用于在 printf 中以正确的格式打印 intmax_t 类型的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值