26 comments

[ 2.9 ms ] story [ 64.8 ms ] thread
One of these reminded me of my woeful attempts at IOCCC-like code, a Hello World implementation:

http://students.mimuw.edu.pl/~dj189395/nhp/czynic/programy/t...

That was fun to read. Why do you need a terminating null (juz)?
It isn't actually used in printf; there's one more argument than there are %c specifiers. gcc -Wall warns about that.
Good catch! And even if we do add another "%c" to fix it, I don't know why we would need to null-terminate a null-terminated string.
If you added another %c, it would print the NUL to stdout; it doesn't terminate anything.
Anyone else spot the uninitialized variable in the banner program?

Also:

> What's the output of the following program. (No, it's not 10!!!)

What's the point of including a program that calls malloc without including a prototype for it, after asking the reader earlier answer why such a program segfaults on IA-64 but not IA-32?

(The answer is: a constraint is violated, requiring a diagnostic, because an int * object pointer is assigned the return value of malloc whose implicit declaration marks it as returning int. The comment issue is a red herring.)

I can't figure out why is it checking `pr` in the while loop. Perhaps it's simply been left over from an earlier version.
That's funny, I added the include for stdlib so couldn't find the issue with the malloc one.
> Write a C program which prints Hello World! without using a semicolon

Here's one way, cheating a bit by abusing the definition of "a C program which prints":

    /tmp$ gcc hw.c
    hw.c:1:2: warning: #warning Hello world! [-Wcpp]
     #warning Hello world!
A more serious solution, though:

    /tmp$ cat hw.c
    #include <stdio.h>
    void main(void)
    {
        if (puts("Hello world!")) {}
    }
    /tmp$ gcc hw.c
    /tmp$ ./a.out
    Hello world!
How about this one? It's not standards-compliant, but GCC swallows it even with -Wall (or at least it used to), and it's by far my favourite:

    int main(int args, char *argv[printf("Hello, world!")]) { }
Nice one but:

>It's not standards-compliant

Why? It's just a VLA.

Per 5.1.2.2.1 [C11 Final Draft], the `main` function should be declared as either

    int main(void) { /* ... */ }
or

    int main(int argc, char *argv[]) { /* ... */ }
You missed the relevant text:

>or equivalent; 10)

>10) Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as char * * argv, and so on.

And Standard also says that:

>or in some other implementation-defined manner.

and:

>In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined.

So main can be pretty much anything according to the Standard, depending on the system.

(comment deleted)

    #include <stdio.h>
    int main()
    {
     float a = 12.5;
     printf("%d\n", a);
     printf("%d\n", *(int *)&a);
     return 0;
    }
This program invokes undefined behaviour when it casts float\* to int\*, so technically it could print anything, or do something else. I'm not sure how using memcpy instead would impact the puzzle's point though.
Technically, it will print the first N bytes of the float as an int. Where N is the number of bytes ints compile to on the platform. So with knowledge of the platform, the result is entirely predictable.
Actually it invokes undefined behavior when it passes a float to printf() and then tries to print it using %d (which of course expects int). Don't do that.
(comment deleted)
If you know that sizeof(float) >= sizeof(int) the memory alignment should be fine. Also in this case * (int* )&a does not access any bytes outside of an defined data object.

To predict the outcome one have to know the representation of float (IEEE 754) and the big/little endian issues which could be different between floats and ints.

printf("%d\n", a) does promote the value a to a double first (promotion rules) and then either the first or last part i(assuming sizeof(double) == 2*sizeof(int)) is shown depending on little or big endian architecture.

You don't know C.

Reinterpreting an object as an incompatible object is undefined. int and float are incompatible.

C Standard also specifically states that using an incorrect printf format specifier will result in undefined behavior.

I have experimented with printf("%d\n", a); using gcc. When compiled for 32-bit x86, it displays the lower 32 bits of the double (which for 12.5 happen to be all 0).

On x86-64, it displays a random value each time, which actually happens to be some pointer (randomised by ASLR). This is because register passing is used on x86-64, and the the double is stored into a floating point register, but %d reads from a different register.

printf("%d\n", * (int*)&a); produces the number 1095237632 for both x86 and x86-64.

UB according to the standard has no restrictions, but I think the point of these puzzles is to explain what could reasonably happen on a typical system, and why.