无法理解CodeGolf

时间:2017-06-11 12:59:18

标签: c argv puts nul

所以我在StackOverflow上丢失了我的周末,并在this challenge中看到Hot Network Questions

  

背景

     

Hello高尔夫球手!我想学习所有的编程语言!   但我有一个短暂的注意力......并且复制了所有的Hello   世界的例子变得无聊......但我喜欢火! ^ W ^

     

挑战

     

所以这是计划!我希望你们都写出最小的代码   将编译,打印Goodbye Cruel World!,然后崩溃。或者,作为一个   奖金扭曲挑战,打印Hello World!和Goodbye Cruel一起崩溃   世界!

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

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

打印字符串,然后尝试将返回值用作指针   到另一个要打印的字符串,这会导致分段错误。

感谢puts() documentation我发现puts()会在成功时返回非负值。因此,如果我理解正确,这相当于:

puts(2); 

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

后来,这个答案得到了改进:

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

而这次我完全迷失了。因此,i被视为来自main的参数,用于存储puts()的返回值。好。但是\0呢?为什么在那里使用NUL-TERMINATOR字符?

如果你可以请我稍微放松一下,对我来说理解这一点会非常有趣。此外,我认为问题的标题如果改写可能会更准确,但我无法用言语表达我的误解。

3 个答案:

答案 0 :(得分:3)

回答你的第二个问题:

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

'\0'/0之间存在差异 第一个是NUL字符,但第二个是零除。因此,此代码尝试将puts的结果除以零。

答案 1 :(得分:2)

代码失败,因为puts()的参数类型为const char *,表示“指向只读char”。

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

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

答案 2 :(得分:2)

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

第一个解决方案:

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

评估puts("Goodbye Cruel World!"),它会在成功时返回非负值。该值传递给puts()。现在,根据§6.5.2.2 7

  

如果表示被调用函数的表达式具有类型   确实包含一个原型,参数被隐式转换, as   如果通过赋值,则取相应参数的类型   每个参数的类型是其不合格的版本   声明的类型。

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

这是未定义的行为,因为没有为运行此代码时应该发生的行为定义的行为。

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

  

/运算符的结果是来自分区的商   第一个操作数由第二个操作数; %运算符的结果是   剩余。在两个操作中,如果第二个操作数的值是   零,行为未定义。

未定义的行为可能包括也可能不包括"崩溃"你的计划。