【问题标题】:Need explanation for C program outputC程序输出需要说明
【发布时间】:2023-03-07 11:11:01
【问题描述】:

我运行下面的程序,我在输出中得到一个意外的数字。

输入要检查的号码

9999999999

141006540​​7 是质数

我在输出中得到一个不同的数字。我输入了 9999999999,我在输出中看到了 141006540​​7。有人可以解释这是怎么回事吗?看起来我超出了整数域的范围。

#include<stdio.h>
#include<stdlib.h>
void main()
{

    int n,i;

    printf("Enter the number to be checked\n");

    scanf("%d",&n);

     i=2;

    while(i<n)
    {
        if(n%i==0)
        {
            printf("%d is not a prime number\n", n);
            exit(0);
        }

        i++;
    }

    printf("%d is a prime number\n", n);

}

【问题讨论】:

  • void main() 应该是 int main(void)。如果您从一本书中学到了void main(),请退还您的钱并向出版商投诉。

标签: c


【解决方案1】:

9,999,999,999(十个9s)太大了,无法放入您的int 数据类型。如果你算出它和1,410,065,407 之间的区别,你会发现它是 2 的精确幂(准确地说是 233),这意味着它在你扫描它时会缠绕。

我所说的“环绕”是指整数的属性,即简单地环绕在它们的最大值处以成为意想不到的东西。

例如,可以保存值 0..255 的 8 位无符号整数,当您将 1 添加到保存 255 的变量时,将变为零(有符号值倾向于从最大的正值回绕到最小的正值负值,在这种情况下,最小意味着离零最远)。

假设您正在读取一个十进制数,其中类型的范围为0..255,该数字的字符串表示为456。下面的“代码”大概是它的工作原理:

def scanNum(s):
    result = 0
    for each character c in s:
        result = result * 10 + value(c)
    return result
  • 在处理4 字符时,result 乘以十,0 * 10 = 0,然后再加四,得到4。
  • 处理5 字符时,result 乘以十,4 * 10 = 40,然后再加五,得到45。
  • 在处理6 字符时,result 乘以十,45 * 10 = 450 但是, 因为你只能表示0..255,它会回绕,给你450 - 256 = 194。然后你加六,得到200。

您可以在那里看到,您想要的和您得到的之间的差异也与范围非常相关:456 - 200 = 256。

如果您尝试使用 9 9s(或 999,999,999,请记住 32 位二进制补码可表示的最大数在 2,147,483,647 处更大),或使用能够容纳更大数字的数据类型,您可能会发现它没问题:

#include <stdio.h>
#include <stdlib.h>
int main (void) {
    long long n;
    printf("Enter the number to be checked\n");
    scanf("%lld",&n);
    printf("%lld\n", n);
    return 0;
}

根据标准,我最初认为这是错误的,因为根据C11 7.21.6.2 The fscanf function /12,它遵循strtol 函数:

d 匹配一个可选带符号的十进制整数,其格式与 strtol 函数的主题序列的预期格式相同,base 参数的值为 10。对应的参数应该是一个指向有符号整数的指针。

和strtol 函数,C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8 状态:

如果正确值超出可表示值的范围,则返回 LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX 或 ULLONG_MAX(根据返回类型和值的符号,如果有)。

但实际上,只有 格式 遵循该功能。 fscanf 及其兄弟的 结果 由 C11 7.21.6.2 The fscanf function /10 控制:

如果这个对象没有合适的类型,或者如果转换的结果不能在对象中表示,行为是未定义的。

因此,如果超出范围,您返回的数字可以是任何值,事实上,鉴于未定义行为的结果没有限制,它可以做任何事情,而不仅仅是返回一个狡猾的结果,包括格式化你的磁盘和将本地时空坍缩成一个裸奇点:-)

在这种情况下,它似乎只是将值包装为扫描过程的一部分,这是代码要做的最简单的事情。

【讨论】:

  • 是的。区别是 8589934592 是 2 的精确幂。但我没有得到“环绕”这个词。你的意思是一旦达到结束限制它就会回到整数列表的开头?
  • @Zack,是的,基本上就是这样。我已经用(大量)更多信息更新了答案。希望对您有所帮助。
  • 非常感谢您的详细回答。
  • 行为未定义,而不仅仅是结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-12
  • 2016-04-17
  • 2012-12-04
  • 1970-01-01
  • 2016-05-17
  • 1970-01-01
  • 2014-10-08
相关资源
最近更新 更多