scanf对于short int的奇怪行为

时间:2011-12-19 03:29:34

标签: c scanf undefined-behavior

代码如下:

#include <stdio.h>
main()
{
    int m=123;
    int n = 1234;
    short int a;
    a=~0;
    if((a>>5)!=a){
        printf("Logical Shift\n");
        m=0;
    }
    else{
        printf("Arithmetic Shift\n");
        m=1;
    }
    scanf("%d",&a);
    printf("%d\n", m);
}

在行scanf("%d",&a);之后,m的值变为 0

我知道它可能是由scanf引起的:a的类型很短,输入的类型是int。但这怎么会影响m的价值?

非常感谢!

5 个答案:

答案 0 :(得分:7)

您的代码段中m成为0的最可能原因是您指定m在if语句的正文中包含此值,但由于代码包含< em>未定义的行为没有人可以肯定地说。


当scanf期望short*

时传递int*的可能故事

假设sizeof(short) = 2sizeof(int) == 4

当输入主函数时,变量所在的堆栈通常如下所示:

  _
 |short int (a)   : scanf will try to read an int (4 bytes).
 |_ 2 bytes       : This part of memory will most
 |int       (n)   : likely be overwritten
 |                :..
 |
 |_ 4 bytes
 |int       (m)
 |
 |
 |_ 4 bytes

当您将%d(即一个int)读入变量a但不应影响变量m时,尽管n会可能有部分被覆盖。


未定义的行为

虽然这是一个猜谜游戏,因为你在使用scanf语句时调用我们通常所说的“未定义的行为”。

标准不能保证的是UB,结果可能是任何东西。也许您会将数据写入另一个属于不同变量的段,或者您可能会使Universe内爆。

当UB出现时,没有人能保证我们会活着看另一天。


如何使用short int

阅读scanf

使用%hd,一定要传递short* ..我们已经有足够的UB一晚了!

答案 1 :(得分:2)

你是对的,%d期望并写一个int。如果您输入的值小于65535,则它适合short之外的字节,因此当您打印0时会看到a。我试着读short并将其打印出来;我输入了65536123,得到了123,这非常有意义(65536正好占据了16位;你看到剩下的123通过short的两个字节。这种行为很危险,因为short的另外两个字节最终位于short的“变量隔壁”,这非常非常糟糕。我希望这可以说服你不要这样做。

P.S。要使用short阅读scanf,请声明一个临时int变量,使用scanf将值读入其中,然后将其转换为short

答案 2 :(得分:2)

假设intshort分别是您平台上的四字节和两字节整数(这可能是假设,但标准不能保证),您会问{ {1}}读取一个整数并将其存储在四个字节中:scanf的两个字节,以及内存中跟随它的两个字节。 (嗯,从技术上讲,这是未定义的行为,并且没有保证特定的行为;但这是它可能做的事情。)显然你的编译器使用b之后的两个字节作为b的前两个字节。这有点令人惊讶 - 我当然不希望mb相邻,而是暗示你的编译器没有对齐mshort s到四字节块的开头 - 但完全合法。

如果添加

,您可以更好地了解正在发生的事情
int

将显示相对于彼此存储printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m); a的位置。 (就像测试一样,我的意思是。你不会想要“真正的”代码。)

答案 3 :(得分:1)

将指向非int的指针传递给scanf的%d时,您正在调用未定义的行为。

可能,编译器引入了填充字节用于对齐目的,并且值存储在填充字节中而不是“有用”字节。

但是,编译器可以自由地做任何事情,从提出段错误/访问冲突到调用鼻子恶魔。

答案 4 :(得分:1)

如果你实际上使用了变量n,那么它可能是被破坏的那个,而不是m。由于您没有使用n,因此编译器对其进行了优化,这意味着mscanf()编写了4个字节(因为它被告知它有一个字节)指向(4字节)整数的指针,而不是2个字节。这取决于硬件的很多细节,例如字节序和对齐(如果int必须在4字节边界上对齐,你就不会看到问题;我想你是在英特尔机器,而不是PowerPC或SPARC)。

不要误解你的编译器 - 即使是偶然的。它会自己回来。