为啥我不能在c中将字符串转换为int

Posted

技术标签:

【中文标题】为啥我不能在c中将字符串转换为int【英文标题】:why cant I convert a string into an int in c为什么我不能在c中将字符串转换为int 【发布时间】:2021-03-24 09:32:04 【问题描述】:

假设我们有一个char数组input = "12345678987654321",为什么我不能用atoi()把它转换成整数12345678987654321?我已经尝试过了,它返回了一些我无法理解的数字。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() 
    char num[20];
    scanf("%s\n", num);
    printf("%d", atoi(num));
    return 0;

【问题讨论】:

您系统中的sizeof int 是什么? INT_MAX 是什么? 因为atoi 返回int 的大小和范围有限?通常为 32 位,这意味着它的范围从大约负二十亿到正二十亿。 此外,错误检查应从输入以scanf("%19s",num); 开始(并删除换行符)。 float 将无法以良好的方式处理这么高的数字(如果有的话),我不确定double。如果您想要一个大整数,请使用long long,如果支持,它将至少有 64 位。 @welp..., INT_MAX 绝对不是 30。这是机器整数表示的参数,以预处理器宏的形式自动暴露给您的代码。在符合标准的 C 实现中,它不会少于 32767。在如今的大多数实现中,它超过了 20 亿。如果它真的是 30,那么你的 C 实现将被严重削弱。 【参考方案1】:

您得到奇怪输出的原因是输入的数字超出了int 类型可表示的值范围。 atoi() 函数不一定执行错误检查,如果数字不能表示为 int,它实际上具有未定义的行为。你应该改用strtol()

您的程序还有另一个潜在的未定义行为:如果用户输入超过 19 个字符,scanf("%s\n", num) 可能会超出 num 数组的末尾,这会产生可怕的后果。您应该写scanf("%19s", num) 并测试返回值是否存在潜在的转换错误。

这是修改后的版本:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main() 
    char num[20];

    if (scanf("%19s", num) != 1) 
        printf("no input\n");
        return 1;
    
    int c = getchar();
    if (c != '\n' && c != EOF) 
        printf("too many characters\n");
        ungetc(c, stdin);
    
    char *end;
    errno = 0;
    long value = strtol(num, &end, 10);
    if (end == num) 
        printf("input is not a number: %s", num);
     else 
        if (errno != 0) 
            printf("number %s is too large for type long\n", num);
         else
        if (value < INT_MIN || value > INT_MAX) 
            printf("number %ld is too large for type int\n", value);
         else 
            printf("value is %d\n", (int)value);
        
        if (*end != '\0') 
            printf("extra characters after the number: %s", end);
        
    
    return 0;

【讨论】:

【参考方案2】:

对于int,“12345678987654321”通常太大,atoi 不提供错误检测。

您可以使用strto… 函数将字符串中的数字转换为整数类型。下面是一个使用 strtoimax 的示例,它是 C 实现支持的最宽的有符号整数类型。还有其他类型的版本,例如strtoll for long long

strto… 函数提供错误检查功能,因此您可以检测数字何时过大并将其报告给用户或以其他方式处理。

#include <ctype.h>      // For isdigit and isspace.
#include <errno.h>      // For errno.
#include <inttypes.h>   // For stroimax and PRIdMAX.
#include <stdint.h>     // For intmax_t.
#include <stdio.h>      // For printf, fprintf, getchar, and ungetc.
#include <stdlib.h>     // For exit.


int main(void)

    // Read string.
    char string[20];
    int n = 0; // Number of characters read so far.
    while (1)
    
        int c = getchar();

        // When we see the end of the input stream, leave the loop.
        if (c == EOF)
            break;

        /* When we see a white-space character (including new-line), put it
           back into the stream and leave the loop.
        */
        if (isspace(c))
        
            ungetc(c, stdin);
            break;
        

        /* If we see character that is not a space or a digit, complain, unless
           it is the first character and is a sign, "+" or "-".
        */
        if (!isdigit(c) && !(n == 0 && (c == '+' || c == '-')))
        
            fprintf(stderr, "Error, non-digit seen:  \"%c\".\n", c);
            exit(EXIT_FAILURE);
        

        // Record the digit.
        string[n++] = c;

        if (sizeof string / sizeof *string <= n)
        
            fprintf(stderr, "Error, too many digits, can handle only %zu.\n",
                sizeof string / sizeof *string - 1);
                /* ("sizeof *string" is one for ordinary "char" arrays, but
                   using it allows changing to wide character types if
                   desired.)
                */
            exit(EXIT_FAILURE);
        
    ;
    // Finish string with a null character.
    string[n] = '\0';

    // Attempt to convert string to an intmax_t.
    char *end;
    errno = 0; // Initialize error indicator.
    intmax_t number = strtoimax(string, &end, 0);

    /* Test whether the string failed to match the form for a numeral, because
       it was empty or was just a sign with no digits.
    */
    if (end == string)
    
        fprintf(stderr, "Error, numeral has no digits.\n");
        exit(EXIT_FAILURE);
    

    // Check for error.
    if (errno != 0)
    
        fprintf(stderr, "Error, number is too large for intmax_t.\n");
        exit(EXIT_FAILURE);
    

    /* Test whether the whole string was used in the conversion. It should have
       been, considering we ensured each character was a digit, but a check
       like this can be useful in other situations, where we are parsing a
       numeral from a more general string.
    */
    if (*end != 0)
    
        fprintf(stderr, "Error, unexpected character in numeral, \"%c\".\n",
            *end);
        exit(EXIT_FAILURE);
    

    // Print the number.
    printf("The number is %" PRIdMAX ".\n", number);

【讨论】:

@chqrlie:已更新。【参考方案3】:

您输入的“12345678987654321” atoi 在 int 中,而 int 的范围来自 -2,147,483,648 - 2,147,483,647 所以为了修复它使用 atol() l-long,这只是因为范围或者如果您使用的是 32 位机器那么你需要使用 long long int 即 atoll() 而不是 atol() data size

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()

    char num[20];
    scanf("%s",num);
    //printf("\n%s",num);
    printf("\n%ld",atol(num)); //<- atoi(num) to atol(num)
    return 0;

atol()查看更多atoi()atol()atoll()

【讨论】:

您可能对 OP 的 C 实现中的 int 范围是正确的,但不一定。 C 语言允许实现对int 具有更窄和更宽的范围,并且在实际实现中可以看到这两种选择。 @JohnBollinger 哦,我明白但不完全你能给我一些关于你向我解释的文章或阅读材料的东西 atol()atoi() 有同样的问题:如果你把它传递给"fish",你会得到一个有效的整数值。两者都无法报告错误,因为每个可能的返回值都是有效的结果。 @MONUTAMBOLI,在“int 大小”或“int 范围”上进行一点谷歌搜索会得到许多合适的结果。其中,这个:Is the size of C int 2 bytes or 4 bytes? 对于那个特定的问题,请务必至少阅读前三个答案。

以上是关于为啥我不能在c中将字符串转换为int的主要内容,如果未能解决你的问题,请参考以下文章

在c中将字符串转换为int [重复]

在 C 中将字符串数组转换为 Int 数组的最佳方法

在 pentaho 中将 STRING 转换为 INT

如何在 Go 中将 int 值转换为字符串?

在Java中将字符转换为整数

在 Linq 中将 int 转换为字符串到实体的问题