Search code examples
cargvputsnul

Unable to understand this code from CodeGolf


So I was losing my week-end on StackOverflow and saw 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!

As a student willing to understand wholly the C language, I have been very confused when stumbling upon the C answer to this challenge :

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.

Thanks to puts() documentation I have found that puts() returns a non-negative value on success. So if I understood correctly, this is equivalent to something like :

puts(2); 

How 2 is "a pointer to another string to be printed" ??

Later, an improvement has been added to this very same answer :

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

And this time I am totally lost. So i is taken as an argument from main, used to store the return value of puts(). Ok. But what about the \0 ? Why use the NUL-TERMINATOR character there ?

If you could please lighten me up a bit, it would be very interesting for me to understand this. Also, I think the title of the question could be a bit more accurate if rephrased but I haven't been able to put in words my misunderstand.


Solution

  • Both solutions cause undefined behavior.

    The first solution:

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

    evaluates puts("Goodbye Cruel World!"), which returns a non-negative value on success. This value is passed to puts(). Now, according to §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.

    So the code attempts to convert the value returned from the first call to puts(), as if by assignment, to a value of type char *. This is the type of the left operand in the assignment, while the type of the right operand is int. The left operand is a pointer type, so the right operand must be a pointer to a qualified or unqualified version of a compatible type, a pointer to void, or a null pointer constant(§6.5.16.1 1). None of these are true, so this is a constraint violation, and the compiler must issue a warning.

    This is undefined behavior, in the sense that there is no behavior defined for what should occur if you run this code.

    The second solution is also undefined behavior, since division by zero results in undefined behavior (§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.

    Undefined behavior may or may not include the "crashing" of your program.