我正在阅读this问题。在阅读第一个答案后,我无法理解-5 >> 1 = -3
的原因。我还对它进行了一些调整。
您还可以查看代码并输出here。 这是我做的:
#include<stdio.h>
int main(){
printf("5/2 = %d\n",5/2);
printf("5 >> 1 = %d\n",5 >> 1);
printf("5/2 = %lf\n",5/2);
printf("5 >> 1 = %f\n",5 >> 1);
printf("-5/2 = %d\n",-5/2);
printf("-5 >> 1 = %d\n",-5 >> 1);
printf("-5/2 = %f\n",-5/2);
printf("-5 >> 1 = %f\n",-5 >> 1);
return 0;
}
输出:
5/2 = 2
5 >> 1 = 2
5/2 = 2.168831
5 >> 1 = 2.168831
-5/2 = -2
-5 >> 1 = -3
-5/2 = 2.168833
-5 >> 1 = 2.168833
我无法理解5/2 == 2.168831, 5 >> 2 == 2.168831, 5 >> 1 == -3
。
为什么会发生这种情况? (答案很可能是基本的,我错过了一些基本的东西,所以请指导我。)
答案 0 :(得分:9)
-5 / 2
的结果是int
,而不是float
或double
。但是,您的格式说明符为%f
,因此您的int
将解释为为float
,这没有任何意义,因此不稳定的值。您正在做的事情被称为未定义的行为:任何事情都可能发生。
答案 1 :(得分:3)
您看到结果的原因是:
当您传递int
参数但使用double
的printf说明符时(请记住在这种情况下float
转换为double
),那么大多数C实现根据通常的规则传递int
参数,将int
参数传递给可变参数函数(接受不同参数类型的函数),但printf
例程将机器状态解释为如果它传递了double
参数,如下所述。 (这不一定是经常发生的事情;一旦你离开C标准定义的行为,C实现可能会做其他事情。特别是,与优化器的复杂交互会导致令人惊讶的结果。但是,这就是发生的事情。最常见的。你不能依赖它。)
每个计算平台都有一些关于如何传递参数的规则。一个平台可能指定所有参数从右到左被压入堆栈,并且每个参数仅使用所需的字节数放入堆栈。另一个平台可能会指定将参数从左向右推入堆栈,或者将参数填充到下一个四个字节的倍数,以使堆栈保持良好对齐。许多现代平台指定在一般寄存器中传递一定大小的整数参数,在浮点寄存器中传递浮点参数,并在堆栈上传递其他参数。
当printf
看到%f
并查找double
参数,但您已通过int
时,printf
会找到什么?如果此平台将两个参数都推送到堆栈,则printf
会找到int
的位,但它会将这些位解释为double
。这会导致printf
打印由您的int
确定的值,但它与您的int
没有明显的关系,因为这些位在int
的编码中具有完全不同的含义, double
。
如果此平台在一个地方放置int
参数而在另一个地方放置double
参数,那么printf
会找到一些与您{{1}无关的位参数。它们恰好被遗留在,例如,int
参数应该在的浮点寄存器中。这些位只是以前工作的剩余部分。相对于您已通过的double
,您获得的值基本上是随机的。您还可以通过int
获取printf
的八个字节来获取混合,其中包含您传递的double
的四个字节以及附近其他任何内容的四个字节。
多次运行程序时,通常会看到打印的值相同。出现这种情况有两个原因。首先,计算机是机械的。它们以很大程度上确定的方式运行,因此它们会一遍又一遍地执行相同的操作,即使这些事情并非特别设计为按照您使用它们的方式使用。其次,每次启动程序时,操作系统启动时传递给程序的环境大致相同。其大部分内存已清除或从程序文件初始化。某些内存或其他程序状态是从计算机中的其他环境初始化的。该数据可能与程序的运行不同。例如,当前时间显然会随着运行而变化。您的命令历史记录以及命令shell在其环境变量中放置的值也是如此。有时多次运行程序会产生不同的结果。
当您使用某些规范未定义的行为的代码(可能是C规范,编译器规范,机器和操作系统规范或其他文档)时,您不能依赖该代码的行为。 (有可能依赖于由C编译器指定的特定C编译器编译的代码的行为,即使它没有被C标准完全指定。)