无法理解混淆的C代码

时间:2014-03-17 05:06:30

标签: c

我无法理解这一点。请解释一下。

修改:打印:'hello, world!'

#include <stdio.h>

int i;
main()
{
  for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\o, world!\n",'/'/'/'));
  //For loop executes once, calling function read with three arguments.
}

read(j,i,p)
{
  write(j/p+p,i---j,i/i);  //how does it work? like printf?
}

3 个答案:

答案 0 :(得分:25)

你已经失败了:

for({initial expr};{conditional expr};{increment expr})

'{initial expr}'是空白的,所以它什么都不做。 '{conditional expr}'是'i["]<i;++i){--i;}"]'

相同
"]<i;++i){--i;}"[i]

const char* str = "]<i;++i){--i;}";
for (; str[i]; )

所以它循环直到表达式为假(即在字符串末尾命中空值)。

{increment expr}

read('-'-'-',i+++"hell\o, world!\n",'/'/'/')

如果你将阅读参数分解为:

'-' - '-' == char('-') - char('-') == 0

对于参数2,您有:

i+++"hell\o, world!\n"

与以下相同:     i ++ +“地狱\ o,世界!\ n”

因此它递增'i'变量,这意味着for循环将循环条件字符串“]中的字符数

第一次你最终得到:

0 + "hell\o, world!\n"

循环第二次将是1 +“地狱\ o,世界!\ n”等。

所以第二个参数是指向“地狱\ o,世界!\ n”的指针。

第三个参数是:

'/'/'/' == '/' / '/' == char('/') / char('/') == 1

所以第三个参数总是1。

现在我们分解调用write:

的read函数
write(j/p+p,i---j,i/i);

有三个参数,第一个是:

j/p+p where j == 0, p == 1 so 0/1+1 == 1.

如果读取,写入功能1的链接被硬编码以写入标准输出。

要写的第二个参数是

i---j

同样是i-- - j,其中i是指向字符串和j = 0的指针,因为我是后递减的是什么都没做,'- 0'什么都不做,它只是将指针传递给写入函数。

第三个参数是'i / i',它始终为1

因此,对于每次调用'read',它每次都会从“地狱\ o,世界!\ n”字符串中写出一个字符。

答案 1 :(得分:0)

read('-'-'-',i+++"hell\o, world!\n",'/'/'/')

使用第一个参数调用read

'-' - '-'

这就是从自身减去一个字符,即零。

第二个论点是:

i++ + "hell\o, world!\n"

这是字符串常量"hell\o world!\n"中的一个地址,它取决于i的值。

第三个论点是:

'/' / '/'

关于角色自由主义主题的算术再现,这次产生1

而不是正常的read,该调用将转到底部定义的方法,该方法实际执行write

写入的参数1是:

j/p+p

这是0/1 + 1 = 1.

参数2是:

i-- - j

其中撤消了早期字符串文字的转换,评估回字符串"hell\o world..."

第三个论点是:

i/i

即。 1。

因此,读取的净效果是从传入文件描述符1的字符串中写入一个字节。

虽然它应该返回任何内容,但结果以及早期循环的确切行为是未定义的。

for循环中i的下标与写入相同:

*((i) + (the string given))

即。它从该字符串中抓取一个字节。由于i的初始值未定义,因此可能是一个越界访问。

请注意i中的read是本地的,而不是全局的i。所以全局的一个继续递增,一次传递一个字符,直到它在另一个字符串文字中到达终止空值。

如果i被赋予0作为初始值,则此代码将是正确的。

(编辑:正如其他地方所指出的那样,我在这里错了:{{1}}最初为零,因为它是全局的。从运动学角度来看,在运行时给它提供全局定义的初始值,而不是C。给堆栈上的任何东西一个初始值的成本,所以C不会。)

答案 2 :(得分:0)

首先看一下C中readwrite函数的语法及其作用:

ssize_t read(int fildes, void *buf, size_t nbyte); 

read()函数将尝试从与打开文件描述符nbyte关联的文件中读取fildes个字节到buf指向的缓冲区中。

ssize_t write(int fildes, const void *buf, size_t nbyte);  

write()函数将尝试将nbyte字节从buf指向的缓冲区写入与打开文件描述符fildes相关联的文件。

现在,将for循环重写为

for(;i["]<i;++i){--i;}"]; read('-' - '-', i++ + "hell\o, world!\n", '/' / '/'));

i["]<i;++i){--i;}"]开始;

"]<i;++i){--i;}"是一个字符串。在C中,如果

char ch;
char *a = "string";` 

然后你可以写ch = "string"[i],相当于i["string"]a[i] = i[a])。这基本上将string的地址添加到ii被初始化为0,因为它是全局定义的)。因此,i初始化为字符串hell\o, world!\n的起始地址 现在要点是for循环不是只迭代一次!
表达式read('-' - '-', i++ + "hell\o, world!\n", '/' / '/')可以重写为(为方便起见);

read(0, i++ + "hell\o, world!\n", 1)  
                                  ^ read only one byte (one character)   

现在它实际上要做的是调用read并增加i(使用其先前的值)。字符串hell\o, world!的起始地址已添加到i。因此,第一次调用只会打印H。在下一次迭代中,i递增(包含下一个字符的地址),调用read将打印下一个字符。
这将持续到i["]<i;++i){--i;}"]变为false\0)。

整体而言,代码的行为是未定义


UB的解释:

请注意,函数调用f(a,b,c)不能使用逗号运算符以及ab和{{1}的评估顺序未指定

C99也说:

  

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。此外,只能访问先前值以确定要存储的值

因此呼叫

c

调用UB。您无法在同一表达式中修改和使用变量。编译器应该发出警告

[警告]'i'上的操作可能未定义[-Wsequence-point]