为什么这段代码给输出c++吸?它背后的概念是什么?

#include <stdio.h>

double m[] = {7709179928849219.0, 771};

int main() {
    m[1]--?m[0]*=2,main():printf((char*)m);    
}

在这里测试。


当前回答

其他人已经很彻底地解释了这个问题,我想补充一点,根据标准,这是未定义的行为。

c++ 11 3.6.1/3主要功能

函数main不能在程序中使用。main的链接(3.5)是由实现定义的。将main定义为deleted或将main声明为inline、static或constexpr的程序是格式错误的。名称main没有其他保留。[示例:成员函数、类和枚举可以被称为main,其他命名空间中的实体也可以。]-end示例]

其他回答

下面的代码打印c++的Suc;C,所以整个乘法只针对最后两个字母

double m[] = {7709179928849219.0, 0};
printf("%s\n", (char *)m);

它只是建立了一个双数组(16字节),如果解释为字符数组,则会为字符串“c++ Sucks”构建ASCII码。

然而,代码并不是在每个系统上都能工作,它依赖于以下一些未定义的事实:

Double正好有8个字节 字节顺序

其他人已经很彻底地解释了这个问题,我想补充一点,根据标准,这是未定义的行为。

c++ 11 3.6.1/3主要功能

函数main不能在程序中使用。main的链接(3.5)是由实现定义的。将main定义为deleted或将main声明为inline、static或constexpr的程序是格式错误的。名称main没有其他保留。[示例:成员函数、类和枚举可以被称为main,其他命名空间中的实体也可以。]-end示例]

也许理解代码最简单的方法是反向处理。我们将从一个字符串开始打印—为了平衡,我们将使用“c++ Rocks”。关键是:和原版一样,它只有8个字长。因为我们要做(大致)像原来的,并以相反的顺序打印出来,我们将开始把它放在相反的顺序。在我们的第一步中,我们将把这个位模式视为double,并打印出结果:

#include <stdio.h>

char string[] = "skcoR++C";

int main(){
    printf("%f\n", *(double*)string);
}

这将产生3823728713643449.5。所以,我们想要以某种不明显,但很容易逆转的方式来处理它。我将半任意地选择乘256,得到978874550692723072。现在,我们只需要写一些模糊的代码来除以256,然后按相反的顺序打印出每个字节:

#include <stdio.h>

double x [] = { 978874550692723072, 8 };
char *y = (char *)x;

int main(int argc, char **argv){
    if (x[1]) {
        x[0] /= 2;  
        main(--x[1], (char **)++y);
    }
    putchar(*--y);
}

现在我们有很多强制转换,将参数传递给(递归)main,这些参数完全被忽略了(但是求值以获得递增和递减值是非常关键的),当然,这个看起来完全随意的数字掩盖了我们正在做的事情非常简单的事实。

当然,因为重点在于混淆,如果我们喜欢,我们也可以采取更多步骤。例如,我们可以利用短路求值,将if语句转换为单个表达式,因此main的主体看起来像这样:

x[1] && (x[0] /= 2,  main(--x[1], (char **)++y));
putchar(*--y);

对于任何不习惯混淆代码(和/或代码高尔夫)的人来说,这看起来确实非常奇怪——计算并丢弃一些毫无意义的浮点数的逻辑和,以及main的返回值,这甚至没有返回值。更糟糕的是,如果没有意识到(并思考)短路求值是如何工作的,甚至可能无法立即看出它是如何避免无限递归的。

我们的下一步可能是将打印每个字符与查找该字符分开。我们可以很容易地通过生成正确的字符作为main的返回值,并打印出main返回的内容:

x[1] && (x[0] /= 2,  putchar(main(--x[1], (char **)++y)));
return *--y;

至少对我来说,这看起来很混乱,所以我就到此为止。

免责声明:这个答案被发布到问题的原始形式中,它只提到了c++,并包含了一个c++头。问题转换为纯C是由社区完成的,没有来自最初提问者的输入。


从形式上讲,不可能对这个程序进行推理,因为它是病态的(即它不是合法的c++)。它违反了c++ 11[basic.start.main]p3:

函数main不能在程序中使用。

This aside, it relies on the fact that on a typical consumer computer, a double is 8 bytes long, and uses a certain well-known internal representation. The initial values of the array are computed so that when the "algorithm" is performed, the final value of the first double will be such that the internal representation (8 bytes) will be the ASCII codes of the 8 characters C++Sucks. The second element in the array is then 0.0, whose first byte is 0 in the internal representation, making this a valid C-style string. This is then sent to output using printf().

在HW上运行这个,上面的一些不支持将导致垃圾文本(甚至可能是一个越界的访问)。