0

当我为以下程序提供 4 个整数作为输入(比如 a = 10、b = 20、d = 30、e = 40)时,它会计算c = a + b = 0f = d + e = 70。我知道这种不寻常的行为是因为我为 short int 使用了错误的格式说明符,但这里到底发生了什么?为什么最后两个输入的和是正确的,而前两个数字的和总是= 0?

#include<stdio.h>

void main()
{
    short int a, b, c, d, e, f;
    scanf("%d%d%d%d", &a, &b, &e, &d);
    c = a + b;
    f = d + e;
    printf("%d\n%d\n", c, f);
}
4

3 回答 3

3

"%d"是用于读取int. 如果short小于int,"%hd"是读取 a 的正确格式代码short int

于 2012-07-03T19:12:58.760 回答
3

这是未定义的行为,因此如果不详细分析实现,就无法给出“为什么”。但最有可能的是,编译器选择的变量布局导致其中一些在读入其他变量时被破坏。

使用我的编译器,在添加一行打印出 的地址后a, b, d, e,我得到了输出

0
70
0x7fffa94d30ca
0x7fffa94d30c8
0x7fffa94d30c6
0x7fffa94d30c4

所以发生的事情可能是¹

  1. 扫描到a存储 10 的低位字节0x7fffa94d30ca,接下来的三个字节(小端机器)中的 0,覆盖了没有为任何变量分配的堆栈的两个字节,不幸的是没有致命的后果,
  2. 扫描到b的字节中存储 20,在0x7fffa94d30c8接下来的三个字节中存储 0,覆盖分配给 的两个字节a,因此设置a为 0,
  3. 扫描到e(巧妙地按照字母顺序和声明顺序完成)将 30 存储在字节中0x7fffa94d30c4,将 0 存储到接下来的三个字节中,其中最后两个d是分配的位置,但d之后被扫描,所以
  4. 扫描到d的字节中存储 40,在0x7fffa94d30c6接下来的三个字节中存储b0,用 0 覆盖,
  5. 加法c = a + b结果为 0,因为扫描到其他变量时两个变量都被覆盖,加法f = d + e结果为 70,因为扫描后两者都没有de覆盖。

强制c在堆栈上分配,而不是通过打印出它的地址来放置在寄存器中,这导致扫描到d覆盖c而不是,b相应地,加法c = a + b结果为 20。

¹ 由于使用格式说明符扫描到short int变量是未定义的行为%d,任何事情都可能发生,但如果编译器不竭尽全力利用 UB,简单的解释将是正确的。

于 2012-07-03T19:01:28.903 回答
1

发生的事情是“%d”指示 scanf 从输入中读取和解析一个数字,将其转换为 int,并将该 int 存储在相应的地址。通常,一个 int 是 32 位(四个字节),一个短 int 是 16 位(两个字节),尽管这可能会有所不同。

尽管 scanf 为每个“%d”写入四个字节,但您只为 &a、&b、&e 和 &d 中的每一个传递了两个字节。所以两个字节到正确的地址,另外两个到 &a、&b、&e 和 &d 旁边的任何地方。其中一些彼此相邻并被杂散字节覆盖。

显然,这是非常糟糕的代码,永远不应该使用。您不能指望哪些变量将在内存中的其他变量旁边。

于 2012-07-03T20:25:38.553 回答