我无法理解这一点。请解释一下。
修改:打印:'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?
}
答案 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中read
和write
函数的语法及其作用:
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
的地址添加到i
(i
被初始化为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)
不能使用逗号运算符以及a
,b
和{{1}的评估顺序未指定。
C99也说:
在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。此外,只能访问先前值以确定要存储的值。
因此呼叫
c
调用UB。您无法在同一表达式中修改和使用变量。编译器应该发出警告
[警告]'i'上的操作可能未定义[-Wsequence-point]