这些四行棘手的C代码背后的概念
为什么这段代码会输出C++Sucks
? 它背后的概念是什么?
#include <stdio.h>
double m[] = {7709179928849219.0, 771};
int main() {
m[1]--?m[0]*=2,main():printf((char*)m);
}
在这里测试。
数字7709179928849219.0
具有以下二进制表示形式作为64位double
7709179928849219.0
型:
01000011 00111011 01100011 01110101 01010011 00101011 00101011 01000011
+^^^^^^^ ^^^^---- -------- -------- -------- -------- -------- --------
+
显示标志的位置; 指数的^
,以及-
尾数(即没有指数的值)。
由于该表示使用二进制指数和尾数,所以数字加倍会使指数增加1。 你的程序精确地执行了771次,因此以1075开始的指数( 10000110011
十进制表示)最终变成1075 + 771 = 1846; 1846的二进制表示是11100110110
。 生成的模式如下所示:
01110011 01101011 01100011 01110101 01010011 00101011 00101011 01000011
-------- -------- -------- -------- -------- -------- -------- --------
0x73 's' 0x6B 'k' 0x63 'c' 0x75 'u' 0x53 'S' 0x2B '+' 0x2B '+' 0x43 'C'
这种模式对应于您看到打印的字符串,只是向后。 同时,数组的第二个元素变为零,提供空终止符,使字符串适合传递给printf()
。
更可读的版本:
double m[2] = {7709179928849219.0, 771};
// m[0] = 7709179928849219.0;
// m[1] = 771;
int main()
{
if (m[1]-- != 0)
{
m[0] *= 2;
main();
}
else
{
printf((char*) m);
}
}
它递归调用main()
771次。
在开始时, m[0] = 7709179928849219.0
,它代表C++Suc;C
在每次通话中, m[0]
都会翻倍,以“修复”最后两个字母。 在最后一次调用中, m[0]
包含C++Sucks
ASCII字符表示,而m[1]
仅包含零,因此它具有C++Sucks
字符串的空终止符。 所有假设m[0]
存储在8个字节,所以每个字符需要1个字节。
没有递归和非法的main()
调用它将看起来像这样:
double m[] = {7709179928849219.0, 0};
for (int i = 0; i < 771; i++)
{
m[0] *= 2;
}
printf((char*) m);
免责声明:这个答案已发布到问题的原始形式,仅提及C ++并包含C ++头文件。 问题转化为纯C是由社区完成的,没有原始提问者的意见。
从形式上讲,这个程序的推理是不可能的,因为它是不合格的(即它不是合法的C ++)。 它违反了C ++ 11 [basic.start.main] p3:
函数main不能在程序中使用。
除此之外,它依赖于这样的事实:在典型的消费者计算机上, double
长度为8字节,并且使用某种众所周知的内部表示。 计算数组的初始值,以便在执行“算法”时,第一个double
的最终值将使内部表示(8字节)成为8个字符C++Sucks
的ASCII代码。 数组中的第二个元素为0.0
,其内部表示中的第一个字节为0
,使其成为有效的C风格字符串。 然后使用printf()
将其发送到输出。
如果在硬件上运行上述某些操作,则会导致垃圾文本(或者甚至是访问超出边界)。
链接地址: http://www.djcxy.com/p/73599.html