使用C将字符串常数转换为数字值



我编写了一个C程序,该程序使用两种不同的算法将代表数字值的字符串常数转换为其整数值。由于某些原因,第一种算法ATOI()在大量值上无法正确执行,而第二算法Atoi_imp()可以正常工作。这是优化问题还是其他错误?问题在于,第一个功能使程序的过程终止错误。

#include <stdio.h>
#include <string.h>
unsigned long long int atoi(const char[]);
unsigned long long int atoi_imp(const char[]);
int main(void) {
    printf("%llun", atoi("9417820179"));
    printf("%llun", atoi_imp("9417820179"));
    return 0;
}
unsigned long long int atoi(const char str[]) {
    unsigned long long int i, j, power, num = 0;
    for (i = strlen(str) - 1; i >= 0; --i) {
        power = 1;
        for (j = 0; j < strlen(str) - i - 1; ++j) {
            power *= 10;
        }
        num += (str[i] - '0') * power;
    }
    return num;
}
unsigned long long int atoi_imp(const char str[]) {
    unsigned long long int i, num = 0;
    for (i = 0; str[i] >= '0' && str[i] <= '9'; ++i) {
        num = num * 10 + (str[i] - '0');
    }
    return num;
}

atoi是C标准库的一部分,签名int atoi(const char *);

您正在声明具有该名称的功能,但会给它不同的返回类型。请注意,在C中,函数名称是唯一重要的东西,工具链只能相信您在源代码中讲述的内容。如果您对编译器撒谎,就像这里一样,所有赌注都关闭了。

您应该为自己的实现选择其他名称以避免问题。


@pmg,C标准(链接到C99.7.1.3)所研究,使用C标准库中的名称对您自己的全局符号(函数或全局变量)明确是 undevined行为。当心鼻恶魔!

好吧,您的功能ATOI至少有一个问题。
您正在以无符号值进行循环,并检查其是否更大的零,这应该是底流。

最简单的修复是索引转移,即:

unsigned long long int my_atoi(const char str[]) {
    unsigned long long int i, j, power, num = 0;
    for (i = strlen(str); i != 0; --i) {
        power = 1;
        for (j = 0; j < strlen(str) - i; ++j) {
            power *= 10;
        }
        num += (str[i-1] - '0') * power;
    }
    return num;
}

为时已晚,但可能会有所帮助。我为基础10做了,如果您更改基础,则需要注意如何计算Digit 0,在*p-'0'中。

我将使用霍纳的规则计算值。

#include <stdio.h>
void main(void)
{
  char *a = "5363", *p = a;
  int unsigned base = 10;
  long unsigned x = 0;
  while(*p) {
    x*=base;
    x+=(*p-'0');
    p++;
  }
  printf("%lun", x);
}

您的功能具有无限的循环:由于i未签名,i >= 0始终是正确的。

可以通过不同的方式进行改进:

  • 您应该仅计算一次str的长度。strlen()并不便宜,它必须扫描字符串,直到找到空终止器。编译器并不总是能够优化相同参数的冗余调用。

  • power可以逐步计算,避免需要嵌套环。

  • 您不应使用名称atoi,因为它是C库中的标准函数。除非您准确正确地实现其规范,否则应使用其他名称。

这是一个更正和改进的版本:

unsigned long long int atoi_power(const char str[]) {
    size_t i, len = strlen(str);
    unsigned long long int power = 1, num = 0;
    for (i = len; i-- > 0; ) {
        num += (str[i] - '0') * power;
        power *= 10;
    }
    return num;
}

以这种方式修改,该功能的性能应与atoi_imp版本相似。但是请注意,它们没有实施相同的语义。atoi_pow必须给出一串数字,而atoi_imp可以具有落后字符。

事实上,atoi_impatoi_pow均未实现atoi的规范,以处理较大的无符号整数:

  • atoi忽略了任何领先的白空间字符
  • atoi接受可选标志,即'+''-'
  • atoi消耗所有以下十进制数字,溢出的行为是未定义的。
  • atoi忽略并不是十进制数字的尾随字符。

鉴于这些语义,自然实现或atoi是带有额外测试的atoi_imp。请注意,即使是strtoull(),您也可以用来实现功能处理空间和一个可选符号,尽管负值的转换可能会带来令人惊讶的结果。

最新更新