Live data from Hacker News

I Do Not Know C: Short quiz on undefined behavior (2015)

kukuruku.co

121–130 of 189 posts

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#122

I'll be honest, I didn't find any of these to be particularly surprising. If you've been using C and are familiar with strict-aliasing and common UB issues I wouldn't expect any of these questions to seriously trip you up. Number 2 is probably the one most people are unlikely to guess, but that example has also been beaten to death so much since it started happening that I think lots of people (Or at least, the peopl…

Yes, like most of the "undefined behaviour allows your computer to format the disk"-style posts this one seems to be written by a programmer with novice-intermediate C knowledge.

What irks me is the intro >> The purpose of this article is to make everyone (especially C programmers) say: “I do not know C”. I think the purpose of the article was mainly for the author to write down some things he learned. Apparently it was his expectation that readers wouldn't be able to answer the quiz.

However, if you can't answer (at least most) of these questions correctly you're _not_ an expert c programmer.

So I think the correct intro here should be "The purpose of this blog post is to to show that if you want to learn C, you actually have to learn it and should not attempt to 'wing it'".

...and maybe also that you should not write patronizing blog posts about a topic which you haven't fully grasped yet yourself.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#123
post #93

Earlier quoted context omitted.

Ok, so you want compilers to generate a translation for program #2 that works "correctly" to your mind when x is null. Please explain to the class the meaning of the construct int y = *x; when x is null, so that all conforming C compilers can be updated to generate code for this case "correctly".

Something like assert(x); int y = *x; is probably closer to the intended meaning. Checking if(!x) afterwards is still dead code, but at least the program is guaranteed to fail in a defined manner. Of course, if implemented this way, every dereference would have to be paired with an assert call, bringing the performance down to the level of Java. (While bringing the memory safety up to the level of Java.)

If the code for program #2 isn't enough to describe the desired behaviour, perhaps it isn't expressible in terms of standard C.

Here's what "x86-64 clang 3.9.1" gives for foo with -O3: (https://godbolt.org/g/0xe1OD)

    foo(int*):                               # @foo(int*)
            test    rdi, rdi
            je      .LBB0_1
            jmp     bar()                 # TAILCALL
    .LBB0_1:
            ret
(You might like to compare this with the article's claims.)

More the sort of thing that I'd expect to be generated (since it's a more accurate rendering of the C code):

    foo(int*):                               # @foo(int*)
            mov     eax, [rdi]
            test    rdi, rdi
            je      .LBB0_1
            jmp     bar()                 # TAILCALL
    .LBB0_1:
            ret
I know that NULL is a valid address on x64 (it's zero), and on any system that doesn't completely suck it will be unmapped. (If I'm using one of the ones that sucks, I'll already be prepared for this. But luckily I'm not.) So I'd like to feel confident that the compiler will just leave the dereferences in - that way, when I make some horrid mistake, I'll at least get an error at runtime.

But rather than compile my mistakes as written, it seems that the compiler would prefer to double down on them.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#124
post #52
post #17

Earlier quoted context omitted.

I don't think #2 has been fully beaten to death yet. Assuming a platform where you don't segfault (say that 'page 0' variables are valid) and thus runtime does proceed; I still can't think of any /valid/ reason to eliminate the if that follows (focus line 2 in the comments). Under what set of logic does being able to de-reference a pointer confer that it's value is not 0 (which is what the test equates to)? In my opi…

Under what set of logic does being able to de-reference a pointer confer that it's value is not 0 (which is what the test equates to)? Normal deductive logic? * No NULL pointer can be dereferenced. * x is dereferenced. * Therefore, x is not a NULL pointer. Of course, the compiler is presuming that your code is correct. That's a reasonable presumption when dealing with computer programming languages. Programming langu…

* No NULL pointer can be dereferenced.

NULL pointers CAN be dereferenced, it all depends on the environment you run on.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#126
post #123
post #93

Earlier quoted context omitted.

Something like assert(x); int y = *x; is probably closer to the intended meaning. Checking if(!x) afterwards is still dead code, but at least the program is guaranteed to fail in a defined manner. Of course, if implemented this way, every dereference would have to be paired with an assert call, bringing the performance down to the level of Java. (While bringing the memory safety up to the level of Java.)

If the code for program #2 isn't enough to describe the desired behaviour, perhaps it isn't expressible in terms of standard C. Here's what "x86-64 clang 3.9.1" gives for foo with -O3: ( https://godbolt.org/g/0xe1OD ) foo(int*): # @foo(int*) test rdi, rdi je .LBB0_1 jmp bar() # TAILCALL .LBB0_1: ret (You might like to compare this with the article's claims.) More the sort of thing that I'd expect to be generated (sin…

If you want the compiler to leave the dereferences in, use `-fno-delete-null-pointer-checks` or ask your compiler vendor for an equivalent option. Compilers delete null pointer checks by default (on typical platforms) because it's what most users want.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#127
post #123

Earlier quoted context omitted.

If the code for program #2 isn't enough to describe the desired behaviour, perhaps it isn't expressible in terms of standard C. Here's what "x86-64 clang 3.9.1" gives for foo with -O3: ( https://godbolt.org/g/0xe1OD ) foo(int*): # @foo(int*) test rdi, rdi je .LBB0_1 jmp bar() # TAILCALL .LBB0_1: ret (You might like to compare this with the article's claims.) More the sort of thing that I'd expect to be generated (sin…

If you want the compiler to leave the dereferences in, use `-fno-delete-null-pointer-checks` or ask your compiler vendor for an equivalent option. Compilers delete null pointer checks by default (on typical platforms) because it's what most users want.

But the null pointer check was left in. (And, sure enough, adding -fno-delete-null-pointer-checks makes no difference.)

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#128
post #123

Earlier quoted context omitted.

If the code for program #2 isn't enough to describe the desired behaviour, perhaps it isn't expressible in terms of standard C. Here's what "x86-64 clang 3.9.1" gives for foo with -O3: ( https://godbolt.org/g/0xe1OD ) foo(int*): # @foo(int*) test rdi, rdi je .LBB0_1 jmp bar() # TAILCALL .LBB0_1: ret (You might like to compare this with the article's claims.) More the sort of thing that I'd expect to be generated (sin…

If you want the compiler to leave the dereferences in, use `-fno-delete-null-pointer-checks` or ask your compiler vendor for an equivalent option. Compilers delete null pointer checks by default (on typical platforms) because it's what most users want.

> Compilers delete null pointer checks by default (on typical platforms) because it's what most users want.

It's what users think they want (it leads to e.g. higher numbers on meaningless microbenchmarks).

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#129
post #20

Earlier quoted context omitted.

> Under what set of logic does being able to de-reference a pointer confer that it's value is not 0 (which is what the test equates to)? Simple: undefined behavior makes all physically possible behaviors permissible. In reality though, such an elimination would only be correct if the compiler was able to prove that the function is ever called with NULL, and if the compiler is smart enough to do that, hopefully the co…

This is a kind of Department of Motor Vehicles Bureaucrat thinking. For example, there as many thousands of lines of C code that reference *0, which is a perfectly good address in some environments. One should be able to depend on compilers following expressed intentions of the programmer and not making silly deductions based on counter-factual assumptions.

This battle has been fought and lost. If you require sensible behaviour, just move on and use a language that offers it. C compilers will do what makes them look good on benchmarks, and various "friendly C" efforts have been tried and failed.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#130
post #28

My 'favourite' bit of surprising (not undefined) behaviour I've seen recently in the C11 spec is around infinite loops, where void foo() { while (1) {} } will loop forever, but void foo(int i) { while (i) {} } is permitted to terminate...even if i is 1: > An iteration statement whose controlling expression is not a constant expression, that performs no input/output operations, does not access volatile objects, and pe…

This definition is actually required for the correctness of many standard compiler optimizations such as partial redundancy elimination and code motion.
Post reply on HN