scanf对于short int的奇怪行为

代码如下:

#include  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的价值呢?

非常感谢 !

在你的代码片段中m0的最可能原因是因为你指定m在if语句的主体中有这个值,但由于代码包含未定义的行为,所以没有人可以肯定地说。


当scanf期望int*时传递short*的可能故事

假设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的变量a ,尽管n很可能会覆盖它的一部分。


未定义的行为

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

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

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


如何使用scanf读取short int

使用%hd ,并确保通过它short* …我们已经有足够的UB一晚!

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

如果添加,您可以更好地了解正在发生的事情

 printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m); 

它将显示相对于彼此存储am位置。 (就像测试一样,我的意思是。你不会想要“真正的”代码。)

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

PS要使用scanf读取short ,请声明一个临时int变量,使用scanf将值读入其中,然后将其转换为short

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

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

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

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

不要误解你的编译器 – 即使是意外。 它将自己回来。