探秘atoi与atof的模拟之路:从原理到实践的全能指南!

目录

 ​编辑

一.atoi及atof库函数的工作原理

1.1atoi

1.2atof

1.3使用时的注意事项

注意事项

1. 检查输入字符串是否为 NULL

2. 检查字符串是否仅包含有效的数字字符

3. 检查转换结果是否在预期范围内

4. 使用更健壮的替代函数

二. 模拟实现atoi和atof

2.1模拟 atoi 和 atof 函数时的思路和注意事项

1.模拟 atoi 函数的思路

2.模拟 atof 函数的思路

3.注意事项

2.2 atoi的模拟实现 

2.3 atof的模拟实现

 三. 完结散花


 

                                            悟已往之不谏,知来者犹可追  

创作不易,宝子们!如果这篇文章对你们有帮助的话,别忘了给个免费的赞哟~

一.atoi及atof库函数的工作原理

在C语言编程中,字符串与数值之间的转换是常见的操作。为了简化这一操作,C标准库提供了一系列函数,其中atoi和atof是两个非常重要的函数它们分别用于将字符串转换为整数和浮点数

1.1atoi

首先,我们来看看atoi函数。atoi是“ASCII to integer”的缩写,它的主要功能是将一个ASCII码表示的字符串转换成整数。函数原型为int atoi(const char *nptr);,其中nptr是指向要转换的字符串的指针。这个函数会扫描整个字符串,跳过前面的空白字符(例如空格,制表符,换行符等),直到遇到第一个非空白字符为止。然后,从该字符开始,对连续的数字字符进行解析,直到遇到非数字字符为止。最后,将解析得到的数字字符转换为对应的整数,并返回该整数。如果字符串无法被解析为一个有效的整数,或者表示的数值超出了int类型的表示范围,那么atoi函数的行为是未定义的。因此,在使用atoi函数时,需要确保输入的字符串是有效的,且表示的数值在int类型的表示范围内。

以下是对atoiatof函数更具体的解释,并附带了一些示例代码,以帮助您更好地理解它们的用法。

#include <stdio.h>  
#include <stdlib.h>  
  
int main() {  
    const char *str1 = "12345";  
    const char *str2 = "  -6789"; // 前面有空格  
    const char *str3 = "abc123";  // 非数字开头  
    const char *str4 = "12345678901234567890"; // 超出int范围  
  
    int num1 = atoi(str1);  
    int num2 = atoi(str2);  
    int num3 = atoi(str3); // 将返回0,因为字符串不是以数字开头  
    int num4 = atoi(str4); // 超出int范围的行为是未定义的  
  
    printf("num1 = %d\n", num1); // 输出: num1 = 12345  
    printf("num2 = %d\n", num2); // 输出: num2 = -6789  
    printf("num3 = %d\n", num3); // 输出: num3 = 0  
    printf("num4 = %d\n", num4); // 输出可能不正确,因为超出了int的范围  
  
    return 0;  
}

在这个例子中,atoi 函数成功地将 str1 和 str2 转换为整数,但 str3 不能被转换为整数,因为它不是以数字字符开始的,所以 atoi 返回0。str4 表示的数字超出了 int 类型的范围,因此结果将是未定义的。

1.2atof

接下来,我们看看atof函数。atof是“ASCII to floating point”的缩写,它的功能是将一个ASCII码表示的字符串转换成浮点数。函数原型为double atof(const char *nptr);,其中nptr同样是指向要转换的字符串的指针。atof函数的处理方式与atoi类似,也是先跳过字符串前面的空白字符,然后从第一个非空白字符开始解析数字字符,直到遇到非数字字符为止不同的是,atof函数在解析数字字符时,会同时解析可能存在的正负号、小数点以及指数部分,以得到完整的浮点数表示。然后,将解析得到的数字字符转换为对应的浮点数,并返回该浮点数。如果字符串无法被解析为一个有效的浮点数,那么atof函数会返回0.0。

#include <stdio.h>  
#include <stdlib.h>  
  
int main() {  
    const char *str1 = "123.45";  
    const char *str2 = "  -678.90"; // 前面有空格  
    const char *str3 = "abc123.45"; // 非数字开头  
    const char *str4 = "1.23e-4";   // 科学计数法  
  
    double num1 = atof(str1);  
    double num2 = atof(str2);  
    double num3 = atof(str3); // 将返回0.0,因为字符串不是以数字开头  
    double num4 = atof(str4); // 可以解析科学计数法  
  
    printf("num1 = %f\n", num1); // 输出: num1 = 123.450000  
    printf("num2 = %f\n", num2); // 输出: num2 = -678.900000  
    printf("num3 = %f\n", num3); // 输出: num3 = 0.000000  
    printf("num4 = %f\n", num4); // 输出: num4 = 0.000123  
  
    return 0;  
}

在这个例子中,atof 函数成功地将 str1str2 和 str4 转换为浮点数。但是,由于 str3 不是以数字字符开始的,atof 函数返回了0.0。注意,atof 还可以解析科学计数法表示的浮点数,如 str4 所示。

1.3使用时的注意事项

注意事项

  • 使用 atoi 和 atof 时,应确保输入字符串是有效的,并且表示的数值在相应类型的表示范围内。
  • 这两个函数不会检查错误,比如输入字符串是否为 NULL 或者是否包含非数字字符。在实际应用中,你可能需要添加额外的错误检查逻辑。
  • 这两个函数在处理大数或异常输入时可能会产生不可预测的结果,因此在需要精确处理数值转换的场景中,建议使用更健壮的解析方法,比如 strtol 和 strtod 函数,它们提供了更详细的错误处理机制。

在实际应用中,使用 atoi 和 atof 函数时,确实需要添加额外的错误检查逻辑,以确保输入字符串的合法性以及转换结果的正确性。下面是一些关于如何添加错误检查逻辑的详细建议:

1. 检查输入字符串是否为 NULL

在使用 atoi 或 atof 之前,首先应该检查传入的字符串指针是否为 NULL如果指针为 NULL,直接进行转换将导致程序崩溃

const char *str = /* 获取或定义字符串 */;  
if (str == NULL) {  
    // 处理错误,例如打印错误消息或返回错误代码  
}


2. 检查字符串是否仅包含有效的数字字符

atoi 和 atof 函数会跳过字符串前面的空白字符,并尝试解析后续的字符为数字。但是,它们不会检查字符串中是否包含除数字、正负号、小数点和指数符号之外的其他字符。因此,如果需要更严格的验证,应该手动检查字符串内容。

#include <ctype.h> // 引入字符处理函数  
  
bool is_valid_number(const char *str) {  
    // 检查是否以正负号开头  
    if (*str == '+' || *str == '-') {  
        str++;  
    }  
      
    // 检查后续字符  
    while (*str != '\0') {  
        if (!isdigit(*str) && *str != '.' && (*str != 'e' && *str != 'E') &&  
            (!(*str == '+' || *str == '-') || (str > string && *(str - 1) == 'e' || *(str - 1) == 'E'))) {  
            return false; // 包含非法字符  
        }  
        str++;  
    }  
    return true; // 字符串仅包含有效数字字符  
}  
  
// 在转换之前调用此函数检查字符串  
if (!is_valid_number(str)) {  
    // 处理错误,字符串包含非法字符  
}


3. 检查转换结果是否在预期范围内

即使字符串看起来像是有效的数字,转换结果也可能不在预期范围内。例如,对于 atoi,如果转换的整数超出了 int 类型的表示范围,结果是未定义的。对于 atof,虽然结果通常是可预测的,但如果字符串表示的数值过大或过小,可能会导致精度损失。

int num = atoi(str);  
// 检查转换后的整数是否在int的表示范围内  
if (num > INT_MAX || num < INT_MIN) {  
    // 处理溢出错误  
}  
  
double fnum = atof(str);  
// 对于atof,通常没有直接的溢出检查,但可以检查特殊值或异常大的数值  
if (isinf(fnum) || isnan(fnum)) {  
    // 处理无穷大或非数字错误  
}


4. 使用更健壮的替代函数

如果可能的话,建议使用更健壮的替代函数,如 strtol 和 strtod,它们提供了更详细的错误处理机制。这些函数能够返回转换后的数值以及遇到的第一个非法字符的位置,从而提供了更多的错误信息。

#include <stdlib.h> // 引入 strtol 和 strtod  
  
char *endptr; // 用于存储遇到非法字符的位置  
long int num = strtol(str, &endptr, 10); // 10 表示使用十进制  
  
if (str == endptr) {  
    // 没有字符被转换,字符串不是有效的数字  
} else if (*endptr != '\0') {  
    // 字符串包含非法字符  
}


通过这些额外的错误检查逻辑,你可以增加程序的健壮性,确保在转换字符串为数值时能够正确处理各种异常情况。

二. 模拟实现atoi和atof

2.1模拟 atoi 和 atof 函数时的思路和注意事项

1.模拟 atoi 函数的思路

atoi 函数的主要作用是将一个字符串转换成整型数据。模拟实现这个函数时,可以按照以下步骤进行:

  1. 初始化变量
    • 定义一个 int 类型的变量 result,用于存储转换结果,初始化为 0。
    • 定义一个 int 类型的变量 sign,用于表示数字的符号,初始化为 1(表示正数)。
  2. 处理符号
    • 检查字符串的第一个字符是否为负号 '-'。如果是,将 sign 设置为 -1,并将字符串指针向后移动一位。
  3. 转换数字
    • 遍历字符串中的每个字符,只要字符是数字('0' 到 '9'),就将其转换为对应的整数值,并累加到 result 上。这通常通过字符与 '0' 的 ASCII 值之差来实现。
    • 在累加过程中,要检查 result 是否会溢出。这通常通过比较 result 与 INT_MAX 和 INT_MIN 来实现。
  4. 返回结果
    • 将 result 与 sign 相乘,得到最终的整型结果。
    • 返回结果。

2.模拟 atof 函数的思路

atof 函数的作用是将一个字符串转换成浮点型数据。模拟实现这个函数时,可以按照以下步骤进行:

  1. 初始化变量
    • 定义一个 double 类型的变量 result,用于存储转换结果,初始化为 0.0。
    • 定义一个 int 类型的变量 sign,用于表示数字的符号,初始化为 1(表示正数)。
    • 定义一个 double 类型的变量 fraction,用于处理小数部分,初始化为 0.1。
    • 定义一个 int 类型的变量 exponent 和 exponent_sign,用于处理科学计数法中的指数部分。
  2. 处理符号
    • 检查字符串的第一个字符是否为负号 '-'。如果是,将 sign 设置为 -1,并将字符串指针向后移动一位。
  3. 转换整数部分
    • 遍历字符串中的每个字符,直到遇到小数点或字符串结束。对于每个数字字符,将其转换为对应的整数值,并累加到 result 上。
  4. 转换小数部分
    • 如果字符串中包含小数点,继续遍历小数点后的字符。对于每个数字字符,将其转换为对应的整数值,并与 fraction 相乘后累加到 result 上。每次迭代后,fraction 需要乘以 0.1 以减小权重。
  5. 处理指数部分
    • 如果字符串中包含 'e' 或 'E' 后跟的数字,解析这些数字作为指数,并根据 exponent_sign 的值决定是乘以 10 的正数次幂还是负数次幂。
  6. 返回结果
    • 将 result 与 sign 相乘,得到最终的浮点型结果。
    • 返回结果。

3.注意事项

  1. 溢出处理
    • 在转换过程中,要特别注意整型和浮点型的溢出情况。对于整型,要检查累加结果是否超过 INT_MAX 或 INT_MIN。对于浮点型,虽然 double 类型通常能表示很大范围的数,但还是要避免不合理的计算导致精度损失或溢出。
  2. 错误处理
    • 如果字符串中包含非数字字符(除了可能的符号、小数点和指数表示外),应该考虑如何处理这些情况。你可以选择忽略它们,或者返回一个错误标志。
  3. 空指针检查
    • 在实现这些函数时,要确保输入的字符串指针不是空指针,以避免程序崩溃。
  4. 边界条件测试
    • 在编写完代码后,要进行充分的测试,特别是针对边界条件(如最大/最小整数、最大/最小浮点数、空字符串、只包含符号的字符串等)进行测试。
  5. 性能优化
    • 虽然这些模拟实现的性能可能不是最优的,但对于学习和理解 atoi 和 atof 的工作原理来说已经足够了。在实际应用中,可能会使用更高效的算法或库函数来进行字符串到数字的转换。

2.2 atoi的模拟实现 

#include <ctype.h> // 引入字符处理函数  
  
int my_atoi(const char *str) {  
    int sign = 1;  
    int result = 0;  
  
    // 跳过空白字符  
    while (isspace(*str)) {  
        str++;  
    }  
  
    // 检查符号  
    if (*str == '-') {  
        sign = -1;  
        str++;  
    } else if (*str == '+') {  
        str++;  
    }  
  
    // 转换数字字符  
    while (isdigit(*str)) {  
        if (result > INT_MAX / 10 || (result == INT_MAX / 10 && *str - '0' > 7)) {  
            // 处理溢出情况  
            if (sign == 1) {  
                return INT_MAX;  
            } else {  
                return INT_MIN;  
            }  
        }  
        result = result * 10 + (*str - '0');  
        str++;  
    }  
  
    return sign * result;  
}
if (result > INT_MAX / 10 || (result == INT_MAX / 10 && *str - '0' > 7)) {  
    // 处理溢出情况  
    if (sign == 1) {  
        return INT_MAX;  
    } else {  
        return INT_MIN;  
    }  
}

这段代码是 my_atoi 函数中的一部分,它负责处理整数溢出的情况。在将字符串转换为整数时,我们需要确保结果不会超出 int 类型的表示范围。

让我们逐行解释这段代码:

  1. result > INT_MAX / 10:
    这一条件检查当前的 result 是否已经大于 INT_MAX 除以 10 的结果。INT_MAX / 10 实际上是整数能够表示的最大值去掉最后一位后的数。如果 result 已经大于这个数,那么再加上当前字符代表的数(0-9),结果肯定会溢出。

  2. result == INT_MAX / 10 && *str - '0' > 7:
    这一条件稍微复杂一些。它首先检查 result 是否等于 INT_MAX / 10,这表示 result 已经是整数能够表示的最大值去掉最后一位后的数。然后,它检查当前字符代表的数是否大于 7(即当前字符是否为 '8'、'9')。如果这两个条件都满足,那么将当前字符代表的数加到 result 上也会导致溢出。

一旦上述任一条件为真,代码就进入处理溢出情况的逻辑。

  1. if (sign == 1) { return INT_MAX; } else { return INT_MIN; }:
    这里根据符号 sign 来确定返回的值。如果 sign 为正(即 sign == 1),则返回 INT_MAX。这是因为当尝试将一个大于 INT_MAX 的数转换为 int 时,根据 C 语言的整数溢出行为,结果会是 INT_MAX。类似地,如果 sign 为负,则返回 INT_MIN,因为尝试将一个小于 INT_MIN 的数转换为 int 时,结果会是 INT_MIN

这种处理方式是遵循 C 语言中整数溢出的未定义行为(undefined behavior)的通常做法。在实际编程中,通常建议避免整数溢出,并在可能的情况下进行显式的错误检查和处理。

通过这段代码,我们可以确保 my_atoi 函数在转换过程中不会返回超出 int 范围的值,而是返回该类型的最大值或最小值,这符合 C 语言标准库函数 atoi 的行为。

2.3 atof的模拟实现

#include <math.h> // 引入数学函数  
  
double my_atof(const char *str) {  
    double sign = 1.0;  
    double result = 0.0;  
    double fraction = 0.1;  
    int exponent = 0;  
    int exponent_sign = 1;  
  
    // 跳过空白字符  
    while (isspace(*str)) {  
        str++;  
    }  
  
    // 检查符号  
    if (*str == '-') {  
        sign = -1.0;  
        str++;  
    } else if (*str == '+') {  
        str++;  
    }  
  
    // 转换整数部分  
    while (isdigit(*str)) {  
        result = result * 10.0 + (*str - '0');  
        str++;  
    }  
  
    // 检查小数点  
    if (*str == '.') {  
        str++;  
        // 转换小数部分  
        while (isdigit(*str)) {  
            result += (*str - '0') * fraction;  
            fraction *= 0.1;  
            str++;  
        }  
    }  
  
    // 检查指数部分  
    if ((*str == 'e') || (*str == 'E')) {  
        str++;  
        // 检查指数符号  
        if (*str == '-') {  
            exponent_sign = -1;  
            str++;  
        } else if (*str == '+') {  
            str++;  
        }  
        // 转换指数值  
        while (isdigit(*str)) {  
            exponent = exponent * 10 + (*str - '0');  
            str++;  
        }  
    }  
  
    // 应用指数  
    result *= pow(10.0, exponent_sign * exponent);  
  
    return sign * result;  
}

在 my_atof 函数的模拟实现中,这些变量扮演着重要的角色来执行浮点数转换。下面是每个变量的意义:

  1. double sign = 1.0;
    sign 变量用来存储浮点数的符号。如果字符串表示的数是正数,则 sign 保持为 1.0;如果是负数,则将其设置为 -1.0。在转换的最后阶段,result 会乘以 sign 来得到最终的带符号结果。

  2. double result = 0.0;
    result 变量用来存储转换过程中的累积结果。它初始化为 0.0,并在遍历字符串时根据每个字符的值进行更新。

  3. double fraction = 0.1;
    fraction 变量用于在转换小数部分时逐步减小小数位的权重。它初始化为 0.1,表示小数点后第一位的权重。每次迭代处理一个新的数字字符时,fraction 会乘以 0.1,以便在后续迭代中处理更小的小数位。

  4. int exponent = 0;
    exponent 变量用于存储科学计数法中的指数值。如果字符串中包含 'e' 或 'E' 后跟的数字,这些数字将表示指数的值,并存储在 exponent 中。

  5. int exponent_sign = 1;
    exponent_sign 变量用于存储指数的符号。如果指数是正的,则 exponent_sign 保持为 1;如果是负的,则将其设置为 -1。这将在应用指数时用来确定是将结果乘以 10 的正数次幂还是负数次幂。

在 my_atof 函数的执行过程中,这些变量根据字符串中的字符进行更新,并最终组合起来以计算最终的浮点数值。注意,my_atof 函数的实现中还有一些逻辑来处理整数部分、小数点和小数部分,以及指数的解析和应用。这些变量在这些步骤中扮演着关键的角色。

 三. 完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

  • 130
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 114
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 114
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值