本文翻译自:Concept behind these four lines of tricky C code

Why does this code give the output C++Sucks ? 为什么这段代码给输出C++Sucks What is the concept behind it? 它背后的概念是什么?

#include <stdio.h>double m[] = {7709179928849219.0, 771};int main() {m[1]--?m[0]*=2,main():printf((char*)m);
}

Test it here . 在这里测试一下 。


#1楼

参考:https://stackoom.com/question/1DUgT/这四行棘手的C代码背后的概念


#2楼

It is just building up a double array (16 bytes) which - if interpreted as a char array - build up the ASCII codes for the string "C++Sucks" 它只是构建一个双数组(16个字节) - 如果解释为char数组 - 为字符串“C ++ Sucks”构建ASCII代码

However, the code is not working on each system, it relies on some of the following undefined facts: 但是,代码不能在每个系统上运行,它依赖于以下一些未定义的事实:

  • double has exactly 8 bytes double有8个字节
  • endianness 字节序

#3楼

Disclaimer: This answer was posted to the original form of the question, which mentioned only C++ and included a C++ header. 免责声明:这个答案被发布到问题的原始形式,其中仅提到C ++并包含C ++标题。 The question's conversion to pure C was done by the community, without input from the original asker. 问题转换为纯C是由社区完成的,没有原始提问者的意见。


Formally speaking, it's impossible to reason about this program because it's ill-formed (ie it's not legal C++). 从形式上讲,这个程序是不可能的,因为它是不正确的(即它不是合法的C ++)。 It violates C++11[basic.start.main]p3: 它违反了C ++ 11 [basic.start.main] p3:

The function main shall not be used within a program. 函数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. 除此之外,它依赖于这样的事实:在典型的消费者计算机上, double是8字节长,并且使用某种众所周知的内部表示。 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 . 计算数组的初始值,以便在执行“算法”时,第一个double精度的最终值将使得内部表示(8个字节)将是8个字符C++Sucks的ASCII代码。 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. 数组中的第二个元素是0.0 ,其内部表示中的第一个字节为0 ,使其成为有效的C样式字符串。 This is then sent to output using printf() . 然后使用printf()将其发送到输出。

Running this on HW where some of the above doesn't hold would result in garbage text (or perhaps even an access out of bounds) instead. 在硬件上运行此操作,其中一些上述操作不会导致垃圾文本(或者甚至是访问超出范围)。


#4楼

The code could be re-written like this: 代码可以像这样重写:

void f()
{if (m[1]-- != 0){m[0] *= 2;f();} else {printf((char*)m);}
}

What it's doing is producing a set of bytes in the double array m that happen to correspond to the characters 'C++Sucks' followed by a null-terminator. 它正在做的是在double数组m中产生一组字节,恰好对应于字符'C ++ Sucks',后跟一个空终止符。 They've obfuscated the code by choosing a double value which when doubled 771 times produces, in the standard representation, that set of bytes with the null terminator provided by the second member of the array. 他们通过选择一个double值来模糊代码,当加倍771次时,在标准表示中产生的字节集与数组的第二个成员提供的null终止符相同。

Note that this code wouldn't work under a different endian representation. 请注意,此代码在不同的endian表示下不起作用。 Also, calling main() is not strictly allowed. 此外,不严格允许调用main()


#5楼

More readable version: 更易阅读的版本:

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);}
}

It recursively calls main() 771 times. 它以递归方式调用main() 771次。

In the beginning, m[0] = 7709179928849219.0 , which stands for C++Suc;C . 首先, m[0] = 7709179928849219.0 , 代表 C++Suc;C In every call, m[0] gets doubled, to "repair" last two letters. 在每次通话中, m[0]加倍,以“修复”最后两个字母。 In the last call, m[0] contains ASCII char representation of C++Sucks and m[1] contains only zeros, so it has a null terminator for C++Sucks string. 在最后一次调用中, m[0]包含C++Sucks ASCII字符表示, m[1]仅包含零,因此它具有C++Sucks字符串的空终止符。 All under assumption that m[0] is stored on 8 bytes, so each char takes 1 byte. 所有假设m[0]都存储在8个字节上,因此每个char占用1个字节。

Without recursion and illegal main() calling it will look like this: 没有递归和非法的main()调用它将如下所示:

double m[] = {7709179928849219.0, 0};
for (int i = 0; i < 771; i++)
{m[0] *= 2;
}
printf((char*) m);

#6楼

The number 7709179928849219.0 has the following binary representation as a 64-bit double : 数字7709179928849219.0具有以下二进制表示形式为64位double 7709179928849219.0

01000011 00111011 01100011 01110101 01010011 00101011 00101011 01000011
+^^^^^^^ ^^^^---- -------- -------- -------- -------- -------- --------

+ shows the position of the sign; +表示标志的位置; ^ of the exponent, and - of the mantissa (ie the value without the exponent). 指数的^ ,和-尾数(即没有指数的值)。

Since the representation uses binary exponent and mantissa, doubling the number increments the exponent by one. 由于表示使用二进制指数和尾数,因此将数字加倍会使指数递增1。 Your program does it precisely 771 times, so the exponent which started at 1075 (decimal representation of 10000110011 ) becomes 1075 + 771 = 1846 at the end; 你的程序精确地完成了771次,所以从1075开始的指数(十进制表示10000110011 )最后变为1075 + 771 = 1846; binary representation of 1846 is 11100110110 . 1846年的二进制表示是11100110110 The resultant pattern looks like this: 结果模式如下所示:

01110011 01101011 01100011 01110101 01010011 00101011 00101011 01000011
-------- -------- -------- -------- -------- -------- -------- --------
0x73 's' 0x6B 'k' 0x63 'c' 0x75 'u' 0x53 'S' 0x2B '+' 0x2B '+' 0x43 'C'

This pattern corresponds to the string that you see printed, only backwards. 此模式对应于您看到的打印字符串,仅向后。 At the same time, the second element of the array becomes zero, providing null terminator, making the string suitable for passing to printf() . 同时,数组的第二个元素变为零,提供null终止符,使得该字符串适合传递给printf()

这四行棘手的C代码背后的概念相关推荐

  1. 区块链 java 开源_详细介绍Java区块链开源代码背后的内容

    什么是Java区块链开源代码?它是一种分布式分类帐技术,将所有不同的属性放在一起.那么Java区块链开源代码就可以通过不同的共识机制,使所有添加的数据不变.2015年之前,估计大家都知道的区块链的申请 ...

  2. 清华毕业生开发新特效编程语言:99 行代码背后 20 多年的故事...

    不知道大家看没看<冰雪奇缘2>,反正我看完之后的感觉,就是看两个公主玩了一百分钟的塞尔达...没有任何别的想法... 但有一位清华姚班毕业的大佬 -- 胡渊鸣,在看完之后,试着用 99 行 ...

  3. 让模型理解和推断代码背后的意图是预训练模型的核心挑战 | NPCon演讲实录

    分享嘉宾 | 卢帅 整理 | 王子彧 出品 | CSDN(ID:CSDNnews) 2023 年 3 月 25 日下午,在 CSDN 与<新程序员>联合主办的"新程序员(NPCo ...

  4. 低代码编程核心技术概念

    从技术概念来讲,低代码编程跟通用编程是完全一致的. 要利用好低代码编程工具,至少要掌握下面的技术概念. 低代码编程核心技术概念 一.数据结构 这里的数据结构,指一般意义上的数据表和数据字段. 类似于数 ...

  5. 2019年,4亿行代码背后的故事

    2019年,对于蚂蚁金服而言,"变化"从每一行代码.每一件小事开始悄然生长:多项技术取得全新突破,甚至站上世界的顶峰:智能化程度进一步提升,AI变得更聪明.更安全:开放的技术文化, ...

  6. java主类与源代码名称_Java高级编程基础:类文件结构解析,看穿Class代码背后的秘密...

    类文件结构 在说完了JVM内部结构后,接下来我们需要说一下另外一个非常重要的基础概念Class类结构. 我们既然知道了开发的Java源代码会首先被编译成字节码文件保存,JVM的类加载器会读取这些文件内 ...

  7. 六、科大软件工程(孟宁)(6:子系统的可重用设计和代码背后的思想)

    对于复杂的工程文件,可以使用这个gcc linktable.c menu.c test.c -o menu 但是可以使用makefile做 内容: ![写一个makefile,但是主义的是gcc为ta ...

  8. threadlocal_了解ThreadLocal背后的概念

    threadlocal 介绍 我知道本地线程,但直到最近才真正使用过它. 因此,我开始深入研究该主题,因为我需要一种传播某些用户信息的简便方法 通过Web应用程序的不同层,而无需更改每个调用方法的签名 ...

  9. 了解ThreadLocal背后的概念

    介绍 我知道本地线程,但直到最近才真正使用过它. 因此,我开始深入研究该主题,因为我需要一种传播某些用户信息的简便方法 通过Web应用程序的不同层,而无需更改每个调用方法的签名. 小前提信息 线程是具 ...

最新文章

  1. 自然语言处理的现实应用
  2. SDUSTOJ 1801 LIS2(最长上升子序列不同值的数量)
  3. 关于信噪比的确切含义——Eb/No和SNR的区别
  4. java基础提升篇:Static关键字
  5. basic和python_PythonBasic
  6. 微型计算机频繁死机的原因,电脑经常死机是什么原因|电脑经常死机的解决方法...
  7. 用数据结构c语言写成绩排序,C语言数据结构 快速排序实例详解
  8. SpringBoot—启动报错Exception in thread “main“ java.lang.ClassNotFoundException
  9. mysql-数据表的定义操作
  10. Python开发GUI工具介绍,实战:将图片转化为素描画!
  11. macos 运行linux,MacOS 上运行shell
  12. centos下安装和卸载jdk
  13. 消息驱动式微服务:Spring Cloud Stream RabbitMQ
  14. 在MT4上使用KDJ指标
  15. 家藏四库图书系列书目(经典)
  16. Android开发之自动更换壁纸
  17. ⭐李宏毅2020作业2---logistic regression
  18. 运动会分数统计系统(数据结构)C++
  19. 猪八戒java开发,猪八戒--Java开发
  20. VUE项目打包部署后刷新浏览器不是最新代码

热门文章

  1. [Spark]Spark RDD 指南二 初始化
  2. move.js操作CSS3动画
  3. 通过Spannable对象设置textview的样式
  4. 什么是.NET Framework
  5. GoAhead WebServer 架构
  6. node-webkit笔记
  7. IntelliJ IDEA提示忽略大小写
  8. 使用Xcode7的Instruments检测解决iOS内存泄露
  9. 使用excel2003中的solver解决最优化问题
  10. 负离子程序员的一组未来手绘,酷毙了