使用整数格式说明符而不是short int:为什么代码的行为如下?

时间:2012-07-03 18:58:31

标签: c memory short integer-overflow format-specifiers

当我提供以下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);
}

3 个答案:

答案 0 :(得分:3)

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

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

0
70
0x7fffa94d30ca
0x7fffa94d30c8
0x7fffa94d30c6
0x7fffa94d30c4

所以发生的事情可能是¹

  1. a的低位字节中扫描到0x7fffa94d30ca存储10,在接下来的三个字节(小端机器)中扫描0,覆盖未分配的堆栈的两个字节任何变数,不幸的是没有致命的后果,
  2. 扫描b存储在0x7fffa94d30c8字节中的20,将0写入接下来的三个字节,覆盖为a分配的两个字节,因此将a设置为0,
  3. 扫描到e(巧妙地按字母顺序和声明顺序完成)在0x7fffa94d30c4的字节中存储30,在接下来的三个字节中存储0,其中最后两个是{{1已分配,但之后会扫描d,所以
  4. 扫描d存储在d字节中的40和接下来三个字节中的0,用0覆盖0x7fffa94d30c6
  5. 添加b结果为0,因为在扫描到其他变量时两个变量都被覆盖,而c = a + b添加结果为70,因为f = d + ed都不是扫描后覆盖。
  6. 强制e在堆栈上分配,而不是通过打印出来的地址放在寄存器中,这里扫描到c覆盖d而不是{{1}相应地,添加c导致了20。

    ¹由于使用b格式说明符扫描到c = a + b变量是未定义的行为,所以可能发生任何事情,但如果编译器没有尽力利用UB,那么简单的解释将是正确的。

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

尽管scanf为每个“%d”写入四个字节,但是对于&amp; a,&amp; b,&amp; e和&amp; d中的每一个,它只传递了两个字节。因此,两个字节转到正确的地址,另外两个转到&amp; a,&amp; b,&amp; e和&amp; d旁边的任何一个。它们中的一些彼此相邻并被杂散字节覆盖。

显然,这是非常破碎的代码,永远不应该使用。你不能指望哪些变量将与内存中的其他变量相邻。