为什么高值输入会阻止数组使用 C 中的实际输入值?

Why does a high-value input prevent an array from using the actual input value in C?

我正在制作一个函数,它使用 scanf_s 获取一个值并将其转换为二进制值。该函数运行完美...直到我输入一个非常高的值。

我也在 VS 2019 in x64 in C 中执行此操作

如果重要的话,我正在使用

main(int argc, char* argv[]) 

主要功能。

由于我不确定到底发生了什么,所以这是我猜的全部代码。

BinaryGet()
{
// Declaring lots of stuff
int x, y, z, d, b, c;
int counter = 0;
int doubler = 1;
int getb;
int binarray[2000] = { 0 };

// I only have to change things to 1 now, am't I smart?
int binappend[2000] = { 0 };


// Get number
printf("Gimme a number\n");
scanf_s("%d", &getb);



// Because why not
printf("\n");

// Get the amount of binary places to be used (how many times getb divides by 2)
x = getb;
while (x > 1)
{
    d = x;
    counter += 1;

    // Tried x /= 2, gave me infinity loop ;(
    x = d / 2;
}


// Fill the array with binary values (i.e. 1, 2, 4, 8, 16, 32, etc)
for (b = 1; b <= counter; b++)
{
    binarray[b] = doubler * 2;

    doubler *= 2;

    
}


// Compare the value of getb to binary values, subtract and repeat until getb = 0)
c = getb;
for (y = counter; c >= 1; y--)
{   

    // Printing c at each subtraction

    
    printf("\n%d\n", c);

    // If the value of c (a temp variable) compares right to the binary value, subtract that binary value
    // and put a 1 in that spot in binappend, the 1 and 0 list
    if (c >= binarray[y])
    {

        c -= binarray[y];
        binappend[y] += 1;
    }


    // Prevents buffer under? runs
    if (y <= 0)
    {
        break;
    }
}

// Print the result
for (z = 0; z <= counter; z++)
{
    printf("%d", binappend[z]);
}
}

问题是,当我输入值 999999999999999999(18 位数字)时,它只打印一次 0 并结束函数。数字的值无关紧要,18 个将有相同的结果。

然而,当我输入 17 位数字时,它给了我这个:

99999999999999999

// This is the input value after each subtraction
1569325055

495583231

495583231

227147775

92930047

25821183

25821183

9043967

655359

655359

655359

655359

131071

131071

131071

65535

32767

16383

8191

4095

2047

1023

511

255

127

63

31

15

7

3

1


// This is the binary
1111111111111111100100011011101

它给我的二进制值是31位。我觉得奇怪的是,在 32 这个方便的数字上,它显示出来了,所以我输入了第 32 个二进制位的值减 1 (2,147,483,647),它起作用了。但是加 1 得到 0.

更改数组类型(unsigned int 和 long)并没有改变这一点。也没有更改数组括号中的值。我尝试搜索以查看它是否是 scanf_s 的限制,但一无所获。

我确定(我认为)这不是数组,但可能是我对函数所做的一些愚蠢的事情。有人可以帮忙吗?我给你一个远距离高五

问题确实与您注意到的数字的二次方大小有关,但它在这个调用中:

scanf_s("%d", &getb);

%d 参数意味着它正在读入一个带符号的整数,在您的平台上它可能是 32 位,并且由于它是带符号的,这意味着它可以在正方向上达到 2³¹-1。

scanf() 和相关函数使用的转换说明符可以接受更大尺寸的数据类型。例如 %ld 将接受 long int,而 %lld 将接受 long long int。检查您平台的数据类型大小,因为 long intint 实际上可能大小相同(32 位),例如。在 Windows.

因此,如果您改用 %lld,您应该能够读取更大的数字,达到 long long int 的范围,但请确保更改目标 (getb ) 匹配!此外,如果您对负数不感兴趣,让类型系统帮助您并使用无符号类型:%llu 表示 unsigned long long.

一些细节:

  1. 如果 scanf 或其朋友失败,则 getb 中的值是不确定的,即。未初始化,从中读取是 未定义的行为 (UB)。 UB 是 C 中极其常见的错误来源,您希望避免它。如果 scanf 告诉您它有效,请确保您的代码仅从 getb 读取。

  2. 事实上,一般来说,用 scanf 避免 UB 是不可能的,除非你完全控制了输入(例如,你之前用其他人写出来的,无错误,软件)。虽然您可以检查 scanf 和相关函数的 return 值(它将 return 它转换的字段数),但如果字段太大而无法定义,它的行为是未定义的适合您拥有的数据类型。

  3. scanf etc. here 上有更多详细信息。

  4. 为了避免不知道 int 大小的问题,或者如果 long int 在这个或那个平台上不同,还有 header stdint.h 它定义了特定宽度的整数类型,例如。 int64_t。这些 具有与 scanf() 一起使用的宏,例如 SCNd64。这些从 C99 开始可用,但请注意 Windows 其编译器对 C99 的支持不完整,可能不包括此。

  5. 别对自己太苛刻,你又不傻,C 语言是一门很难掌握的语言,并不遵循自最初设计以来发展起来的现代习语。