这些四行棘手的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

上一篇: Concept behind these four lines of tricky C code

下一篇: Pass a char pointer array to a function in C?