Live data from Hacker News

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

kukuruku.co

141–150 of 189 posts

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

#141
post #131

I'm sorry, but the answer this website gives to 1. is wrong. See for yourself: int i; int i = 10; int main(int argc, char* argv[]){ return 0; } Try to compile it. It doesn't work (gcc.exe (GCC) 5.3.0), the error is: a.cc:2:5: error: redefinition of 'int i' int i = 10; ^ a.cc:1:5: note: 'int i' previously declared here int i; ^ Either I misunderstood the author and this example, or I do know C.

Judging by the .cc extension, you are compiling this with a C++ compiler. Quoting from Annex C (which documents the incompatibilities between C++ and ISO C) of the C++ standard: Change: C++ does not have “tentative definitions” as in C E.g., at file scope, int i; int i; is valid in C, invalid in C++. This makes it impossible to define mutually referential file-local static objects, if initializers are restricted to t…

facepalm of course, even if I use gcc, if I compile a.cc it switches to the c++ compiler. Thanks.

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

#142
post #65

Earlier quoted context omitted.

It's easy to see the problem in the null pointer case. The compiler deduction is that the null test is redundant, but it's not actually redundant. Therefore the compiler "proves" a false theorem. That the standards rules permit the compiler to deduce false things would be, in normal engineering analysis, considered to show a failure in the rules, but the bureaucratic mindset holds that the rules are always, by defini…

> Therefore the compiler "proves" a false theorem. In the axiomatic system implied by the standard, the hypothetical compiler being discussed can prove that the null check can be eliminated. The fact that you believe this axiomatic system is inconvenient does not constitute a refutation of the truth of the theorem. > If the compiler is unable to prove a transformation preserves correctness, it should not do the trans…

the post that brought this to my attention discussed how a security error in Linux was created by this "optimization".

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

#143
post #65

Earlier quoted context omitted.

> Therefore the compiler "proves" a false theorem. In the axiomatic system implied by the standard, the hypothetical compiler being discussed can prove that the null check can be eliminated. The fact that you believe this axiomatic system is inconvenient does not constitute a refutation of the truth of the theorem. > If the compiler is unable to prove a transformation preserves correctness, it should not do the trans…

the post that brought this to my attention discussed how a security error in Linux was created by this "optimization".

This? http://blog.regehr.org/archives/970

The compiler didn't create a security bug by removing the null check. The bug was created by the programmer when he didn't check for null before dereferencing the pointer. Even with the check, the program contained a bug.

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

#144

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…

Not a full-time C programmer, and I was still correct on all of them except #1. Certainly C is more dangerous than other languages, but I don't understand the push to convince people that it is impossible to understand.

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

#145

Earlier quoted context omitted.

I would agree that strict-aliasing is a hurt point for a lot of C devs, which is unfortunate. I'd only suggest that in general, if the strict-aliasing rule is coming into play you're probably already doing some really shady to begin with. Like in this example, casting a `long ` to an `int ` is likely a bad way to go about things even without worrying about strict-aliasing. In a lot of ways, I'd say that problems with…

A common class assignment or interview question is to write your own memcpy. Towards the end you usually start optimizing it by copying multiple bytes at once. That is undefined behavior. You cannot just cast a pointer to uint32_t* and start using it, unless the underlying object is actually uint32_t. In practice it works fine, so people don't care. We'll see what future compilers will do, especially when the homemad…

You can cast from char* to uint32_t* and start using it. I forget the exact standardese, but there is an execption for char*.

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

#146

Honestly, Optimizing compilers will kill C. It killed the one thing C was good at - simplicity (you know exactly what happens where, note I'm not saying speed, as C++ can be quite a bit faster than C). Now, due to language lawyering, you can't just know C and your CPU, you have to know your compiler (and every iteration of it!). And if you slip somewhere, your security checks blow up ( http://blog.regehr.org/archives…

undefined behavior has always been undefined behavior. Optimizing the compiler doesn't change that fact.

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

#147
post #127

Earlier quoted context omitted.

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.)

That's because y is never used, so why should the pointer be dereferenced? If you call bar(y) instead of just bar(), "x86-64 clang 3.9.1" with -O3 does the load as well (but after the check)

    foo(int*):                               # @foo(int*)
        test    rdi, rdi
        je      .LBB0_1
        mov     edi, dword ptr [rdi]
        jmp     bar(int)                 # TAILCALL
    .LBB0_1:
        ret
Only GCC does the kind of aggressive optimization the article mentions (and might need to be tamed by -fno-delete-null-pointer-checks).

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

#148
post #39

Earlier quoted context omitted.

If you want to phrase it like that, a compiler tries to prove conjectures and performs actions (usually code and data elimination) based on whether it can prove them or their negatives. Sometimes it can't do either. I don't see where you're going, though.

It's easy to see the problem in the null pointer case. The compiler deduction is that the null test is redundant, but it's not actually redundant. Therefore the compiler "proves" a false theorem. That the standards rules permit the compiler to deduce false things would be, in normal engineering analysis, considered to show a failure in the rules, but the bureaucratic mindset holds that the rules are always, by defini…

Compilers operate under rules. The rules in this case say the null test is redundant.

You could argue the rules are "wrong", but that's a totally different topic.

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

#149
post #129

Earlier quoted context omitted.

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.

Au contraire - gcc and clang both appear to do the right thing now.

https://godbolt.org/g/lqmNLh

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

#150
post #145

Earlier quoted context omitted.

A common class assignment or interview question is to write your own memcpy. Towards the end you usually start optimizing it by copying multiple bytes at once. That is undefined behavior. You cannot just cast a pointer to uint32_t* and start using it, unless the underlying object is actually uint32_t. In practice it works fine, so people don't care. We'll see what future compilers will do, especially when the homemad…

You can cast from char* to uint32_t* and start using it. I forget the exact standardese, but there is an execption for char*.

There is an exception for accessing any object through a character type pointer, but not the other way around. uint32_t is not a character type, and it doesn't matter if it was casted to a char* first.

Also, apparently uint8_t may not be a character type.

Post reply on HN