采访Hello World解释
这个经典的ioccc条目是用C语言编写的Hello World程序。任何人都可以解释它是如何工作的吗?
原始代码(语法突出显示故意丢失):
int i; main(){for(; i [“] <i; ++ i){ - i;}”]; read(' - ' - ' - ',i +++“hell \ o,world!\ n“,'/'/'/'));} read(j,i,p){write(j / p + p,i --- j,i / i);}
稍微清洁一点:
int i; main() { for ( ; i["]<i;++i){--i;}"]; read('-' - '-', i++ + "hello, world!\n", '/' / '/')); } read(j, i, p) { write(j / p + p, i-- - j, i / i); }
用于循环条件
i["]
这个表达式利用了数组索引在C中可交换的事实。它相当于。
"]
因此,当位置i
处的字符为\0
,即在字符串末尾,即14个字符长(恰好与“hello,world!\ n”相同)时,循环将终止。 因此for
循环条件可以重写为:
i != 14
字符算术
read('-' - '-', i++ + "hello, world!\n", '/' / '/')
char
是整数类型,因此:
-
'-' - '-'
为0 -
'/' / '/'
是1读(0,i ++ +“你好,世界!\ n”,1)
修复所有编译器警告(如隐式int到指针转换),并简化上面提到的事情后,代码变为:
#include int i = 0; void read2(int, char*, int); int main() { while (i != 14) { read2(0, i++ + "hello, world!\n", 1); } return 0; } void read2(int j, char* i, int p) { write(j / p + p, i-- - j, 1); }
(我将read
重命名为read2
以避免与Unix read
函数冲突。)
请注意, read2
的j
和p
参数是不需要的,因为始终使用j = 0和p = 1调用该函数。
#include int i = 0; void read2(char*); int main() { while (i != 14) { read2(i++ + "hello, world!\n"); } return 0; } void read2(char* i) { write(1, i--, 1); }
调用write(1, i--, 1)
i
write(1, i--, 1)
将1个字符从i
写入文件描述符1(stdout)。 并且postdecrement是多余的,因为这是一个从未再次引用的局部变量。 所以这个函数相当于putchar(*i)
。
在主循环中内联read2
函数给出
#include int i = 0; int main() { while (i != 14) { putchar(*(i++ + "hello, world!\n")); } return 0; }
其含义是显而易见的。
不要完全把它拆开,但有一些提示:
-
'-' - '-'
被混淆为0
-
'/' / '/'
和read()中的i / i
被混淆为1
请记住,[]是可交换的,即i["]与
"] (有一个char数组并指向它的第i个元素)。 字符串的内容以任何方式无关紧要,只有其长度用于定义循环的迭代次数。 任何其他长度相同的字符串(在内部,与输出相同的长度......)都可以。 在这个迭代次数之后,“string”[i]返回终止字符串的空字符。 零== false,循环终止。
有了这个,找出其余部分应该比较容易。
编辑: upvotes让我有兴趣再看一下这个。
当然, i++ + "Hello, world!\n"
与"Hello, world!\n"[ i++ ]
。
codelark已经指出0
是stdout的fid。 (给他 + 1,不是我。只是提到完整性。)
read()
的i
当然是本地的,即read()
中的i--
不影响main()
的i
。 由于以下i / i
始终为1
,因此--
运算符根本不执行任何操作。
哦,并告诉采访者解雇编写此代码的人。 它不会对write()
的返回值进行错误检查。 我可以忍受着名的代码写得很糟糕(并且多年来都有这种代码),但是检查错误并不比糟糕的更糟糕,这是错误的 。 🙂
其余的只是一些三年级的数学。 我把它传递给实习生。 😉
i [“…”]的字符串索引导致循环执行字符串的长度。 i ++ + hello world字符串表示每次迭代都会将字符串传递给本地读取函数更深的一个字母。
first iteration =“hello ..”second iteration =“ello ..”
read函数的第一个参数简化为0,stdout的文件描述符。 最后一个参数简化为1,因此每次调用只写入一个字符。 这意味着每次迭代都会写入传递给stdout的字符串的第一个字符。 所以按照上面的示例字符串:
第一次迭代=“h”第二次迭代=“e”
for循环中的字符串索引与hello world字符串的长度相同,并且[]是可交换的,并且字符串为空终止,最后一次迭代将返回空字符并退出循环。
远离C专家,但我会尝试:
i["]
另一个面试问题似乎更多地反映了面试官而不是受访者。 这里的关键问题:* j总是0((’ – ‘ – ‘ – ‘)== 0)* p总是1((’/’/’/’)== 1)* i(在read函数中)是(i ++ +“hello world”)==你好世界中的第i个角色(然后增加i)
因此读取function变为
read(0, NextChar, 1) { write(1, NextChar , 1); }
唯一的另一位是for循环中[]运算符的可交换性。
理解和解析这种代码实际上是毫无价值的。