代码如下:
#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的价值?
非常感谢!
答案 0 :(得分:7)
您的代码段中m
成为0
的最可能原因是您指定m
在if语句的正文中包含此值,但由于代码包含< em>未定义的行为没有人可以肯定地说。
short*
int*
的可能故事
假设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
时,尽管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)
假设int
和short
分别是您平台上的四字节和两字节整数(这可能是假设,但标准不能保证),您会问{ {1}}读取一个整数并将其存储在四个字节中:scanf
的两个字节,以及内存中跟随它的两个字节。 (嗯,从技术上讲,这是未定义的行为,并且没有保证特定的行为;但这是它可能做的事情。)显然你的编译器使用b
之后的两个字节作为b
的前两个字节。这有点令人惊讶 - 我当然不希望m
和b
相邻,而是暗示你的编译器没有对齐m
和short
s到四字节块的开头 - 但完全合法。
如果添加
,您可以更好地了解正在发生的事情int
将显示相对于彼此存储printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m);
和a
的位置。 (就像测试一样,我的意思是。你不会想要“真正的”代码。)
答案 3 :(得分:1)
将指向非int的指针传递给scanf的%d时,您正在调用未定义的行为。
可能,编译器引入了填充字节用于对齐目的,并且值存储在填充字节中而不是“有用”字节。
但是,编译器可以自由地做任何事情,从提出段错误/访问冲突到调用鼻子恶魔。
答案 4 :(得分:1)
如果你实际上使用了变量n
,那么它可能是被破坏的那个,而不是m
。由于您没有使用n
,因此编译器对其进行了优化,这意味着m
被scanf()
编写了4个字节(因为它被告知它有一个字节)指向(4字节)整数的指针,而不是2个字节。这取决于硬件的很多细节,例如字节序和对齐(如果int
必须在4字节边界上对齐,你就不会看到问题;我想你是在英特尔机器,而不是PowerPC或SPARC)。
不要误解你的编译器 - 即使是偶然的。它会自己回来。