无法从 CodeGolf 理解这段代码

Unable to understand this code from CodeGolf

所以我在 Whosebug 上丢失了我的 week-end 并看到了 this challenge in the Hot Network Questions

Background

Hello golfers! I would like to learn all the programming languages! But I kinda have a short attention span... and copying all the Hello World examples gets boring... but I like fire! ^w^

Challenge

So here is the plan! I want you all to write the smallest code that will compile, print Goodbye Cruel World!, and then crash. Or, as a bonus twist challenge, print Hello World! and crash with Goodbye Cruel World!

作为一个愿意完全理解C语言的学生,在遇到这个挑战时C answer我一直很困惑:

main(){puts(puts("Goodbye Cruel World!"));}

Prints the string and then tries to use the return value as a pointer to another string to be printed, which causes a segmentation fault.

感谢 puts() documentation 我发现 puts() return 是 non-negative 成功的价值。所以如果我理解正确的话,这相当于:

puts(2); 

如何 2 是“指向另一个要打印的字符串的指针”??

后来,对这个非常相同的答案进行了改进:

main(i){i=puts("Goodbye Cruel World!")/0;}

这次我完全迷路了。所以i作为main的一个参数,用来存放puts()的return值。行。但是 [=18=] 呢?为什么在那里使用 NUL-TERMINATOR 字符?

如果你能让我轻松一点,理解这一点对我来说会很有趣。另外,我认为如果重新措辞,问题的标题可能会更准确一些,但我无法用语言表达我的误解。

代码失败,因为 puts() 的参数类型是 const char *,意思是 "pointer to read-only char"。

这是静态的,它不会因为你试图传递其他东西而改变,相反,函数将参数值解释为一个指向字符的指针(假设编译器甚至设法编译它,这是一个艰难的假设,因为返回的 int 值不会转换为 const char *).

一般来说,像 2 这样的小整数在 desktop/server-class 系统上作为指针是无效的(也不是在所有嵌入式系统上),即典型进程没有内存可用地址,所以经常发生的是操作系统停止违反其边界的进程。但是,正如评论中提到的,这部分是 undefined.

然后回答你的第二个问题:

main(i){i=puts("Goodbye Cruel World!")/0;}

'[=11=]'/0有区别 第一个是 NUL 字符,第二个是除以零。所以这段代码试图将 puts 的结果除以零。

两种解决方案都会导致未定义的行为。

第一个解决方案:

main(){puts(puts("Goodbye Cruel World!"));}

评估 puts("Goodbye Cruel World!"),其中 returns 是成功的非负值。此值传递给 puts()。现在,根据 §6.5.2.2 7:

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.

因此代码尝试将第一次调用 puts() 的返回值转换为 char * 类型的值,就好像通过赋值一样。这是赋值中左操作数的类型,而右操作数的类型是int。左操作数是指针类型,因此右操作数必须是指向兼容类型的限定或非限定版本的指针、指向 void 的指针或空指针常量 (§6.5.16.1 1)。 None 个为真,因此违反约束,编译器必须发出警告。

这是未定义的行为,从某种意义上说,如果您 运行 这段代码应该发生什么,则没有定义任何行为。

第二种解决方案也是未定义的行为,因为除以零会导致未定义的行为(§6.5.5 5):

The result of the / operator is the quotient from the division of the first operand by the second; the result of the % operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined.

未定义的行为可能包括也可能不包括您程序的 "crashing"。