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的价值呢?
非常感谢 !
在你的代码片段中m
为0
的最可能原因是因为你指定m
在if语句的主体中有这个值,但由于代码包含未定义的行为,所以没有人可以肯定地说。
当scanf期望int*
时传递short*
的可能故事
假设sizeof(short) = 2
且sizeof(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一晚!
假设int
和short
分别是你的平台上的四字节和两字节整数(这可能是假设,但标准不能保证),你要求scanf
读取一个整数并将其存储在四个字节中: b
两个字节,在内存中跟随它的两个字节。 (好吧,从技术上讲,这是未定义的行为,并且没有保证特定的行为;但这是它可能做的事情。)显然你的编译器使用b
之后的两个字节作为m
的前两个字节。 这有点令人惊讶 – 我当然不希望b
和m
相邻,而是暗示你的编译器没有将short
s和int
s对齐到四字节块的开头 – 但完全合法。
如果添加,您可以更好地了解正在发生的事情
printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m);
它将显示相对于彼此存储a
和m
位置。 (就像测试一样,我的意思是。你不会想要“真正的”代码。)
你是对的, %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)。
不要误解你的编译器 – 即使是意外。 它将自己回来。