Live data from Hacker News

Everything in C is undefined behavior

blog.habets.se

441–450 of 748 posts

Re: Everything in C is undefined behavior

#441
post #420

Earlier quoted context omitted.

If you don't follow the standard, gcc -O2 can introduce bugs to your code that you never even wrote. Skipping null checks, executing both branches of a conditional, and so on.

Where did I say I'm not following the standard?

I interpreted these words:

> If you want to be standards correct, yes you have to know the standard well.

to mean that being standards-correct is optional. It's not. Every C programmer needs to know every possible UB by heart and never introduce any of it to their code, or else they'll be constantly introducing subtle, hard to debug bugs that contradict the actual code they wrote.

Maybe you meant something different by those words, but then I'm confused what the "if" was supposed to mean.

Re: Everything in C is undefined behavior

#443
post #381

Earlier quoted context omitted.

Casting to a pointer of incompatible type is UB. The exception is casting to char*.

Tell me why struct* is incompatible with void* when it's such a standard case in C that you don't need a cast: struct foo *x = malloc(sizeof(struct foo)); /* malloc returns void* */ Or rather, tell me why the C11 standards committee decided to declare that struct* is incompatible with a void*

ok so Claude says I was wrong, it's more subtle.

(1) you can cast between any pointer types (no UB - assuming they're aligned), but accessing memory through a wrongly-typed pointer is UB

(2) the only exception is char*, which allows you a "byte view of memory"

(3) calling a function through a pointer requires the parameter pointer types to be compatible, and none of these are: int*, struct foo *, void*, char*

Re: Everything in C is undefined behavior

#444
post #412

Earlier quoted context omitted.

Many, many programmers come to C (and C++) with a lower-level understanding that actually gets in the way here. They understand that all types "are" just bytes and that all pointers "are" just register-sized integer addresses, because that's how the hardware works and has worked for decades. It's perfectly reasonable to expect any load through `int*` to just load 4 bytes from memory, done and done. They get surprised…

I'm not sure that's right. For instance, the Pentium 4 spec explicitly says unaligned int32 loads take longer. And x86/x64 is very gentle in that regard, other archs would whip you. So an unaligned int access is rightfully treated differently. It should be IB. Just creating the pointer, though, should not be UB, even though it apparently is. It should not even be IB.

Also, it’s been way more than a decade since Pentium 4 was remotely relevant.

Re: Everything in C is undefined behavior

#445

Earlier quoted context omitted.

Sure, but the point is that code written against such a compiler is not C and is not portable. It is written in a dialect of C, and that comes with drawbacks. Writing C (or any language) means adhering to the standard, because that's the definition of the language.

You can't make any useful software in "Portable C" - or any portable language for that matter. Side effects matter, and they are always non-portable/implementation defined/dependent on the hardware. What printf() actaully does is implementation defined - what does "printing mean", does a console even exist? Maybe a user expects it to show graphical ascii/utf8 glyphs on a LCD display? Well, not every computer has that…

I agree, that most practical programs will rely on unportable behaviour, but

> What printf() actaully does is implementation defined - what does "printing mean", does a console even exist? Maybe a user expects it to show graphical ascii/utf8 glyphs on a LCD display? Well, not every computer has that, so now what?

You can very well write a program, that doesn't make an assumption about any of those things. In fact you should, because the user is to be the arbiter of in what environment your program gets invoked and what it gets connected to. Writing a program that makes assumptions about the specific behaviour of stdout is going to be highly impractical and annoying and also violates the abstraction and interface that stdout is. This consideration isn't just valid for stdout, but also for any other interface your programs naturally interfaces with.

> Well, not every computer has that, so now what?

In the case stdout is not available or can't process your data it is going to return -1 and set errno and then you can deal with that.

Re: Everything in C is undefined behavior

#446

Earlier quoted context omitted.

No it doesn't. Compilers are only required to emit the read for volatile types. If the type is non-volatile, misaligned, and can be optimised out then it would be perfectly fine to omit it (that would be the "succeed" option).

If a trap is observable behaviour, then the compiler either needs to add code, that checks for the condition and then traps explicitly or it needs to actually perform the read. Currently it can be optimized out, because it is UB.

I think you misunderstood my suggestion. It isn't that misaligned accesses must either all succeed or all fail. That's not possible in general because of MMIO devices.

The suggestion is that each individual access must either succeed or trap. Those are the only possible outcomes, but different accesses can result in different outcomes.

Re: Everything in C is undefined behavior

#447

Earlier quoted context omitted.

Print x twice. Not all “side effects” care about order. Better yet, define an order for parameter evaluation.

You're missing the point. Volatile forces two loads of a value that may have changed in the middle. So the value of "x" may depend on the time/order of load.

Why is that missing the point? Loading it twice, possibly with different values, is the intended behavior. It's only undefined because the C spec doesn't specify the order of the loads (unlike most other languages which have a perfectly well-defined order for side effects in a single expression).

Re: Everything in C is undefined behavior

#448
post #403
post #113

Yes there is tons of surprising and weird UB in C, but this article doesn't do a great job of showcasing it. It barely scratches the surface. Here's a way weirder example: volatile int x = 5; printf("%d in hex is 0x%x.\n", x, x); This is totally fine if x is just an int, but the volatile makes it UB. Why? 5.1.2.4.1 says any volatile access - including just reading it - is a side effect. 6.5.1.2 says that unsequenced…

> In C, we can have a data race on a single thread and without any writes! You need to distinguish between a UB and a race, and I think that's something that discussions of UB miss. Take any C program and compile it. Then disassemble it. You end up with an Assembly program that doesn't have any UB, because Assembly doesn't have UB. UB is a property of a source program, not the executable. It means that the spec for t…

> because Assembly doesn't have UB

To be pedantic, old hardware like 6502 family chips (Commodore 64, Apple II, etc) had illegal instructions which were often used by programmers, but it was completely up to the chip to do whatever it wanted with those like with UB.

Re: Everything in C is undefined behavior

#449
post #113

Yes there is tons of surprising and weird UB in C, but this article doesn't do a great job of showcasing it. It barely scratches the surface. Here's a way weirder example: volatile int x = 5; printf("%d in hex is 0x%x.\n", x, x); This is totally fine if x is just an int, but the volatile makes it UB. Why? 5.1.2.4.1 says any volatile access - including just reading it - is a side effect. 6.5.1.2 says that unsequenced…

Are you sure?

>unsequenced side effects on the same scalar object are UB

>6.5.3.3.8 tells us that the evaluations of function arguments are indeterminately sequenced w.r.t. each other.

Read 5.1.2.4.3:

"If A is not sequenced before or after B, then A and B are unsequenced."

"Evaluations A and B are indeterminately sequenced when A is sequenced either before or after B, but it is unspecified which."

With a footnote saying this:

"9)The executions of unsequenced evaluations can interleave. Indeterminately sequenced evaluations cannot interleave, but can be executed in any order."

I.e the standard makes a distinction between "unsequenced" and "indeterminately sequenced". And with no mention of side effects on "indeterminately sequenced" being UB it leads me to conclude that your example is not UB.

Re: Everything in C is undefined behavior

#450

Earlier quoted context omitted.

A lot of the Central UB can not be defined, because they rely on detection. In order to have a well defined behaviour (by the standard or the compiler) the implementation needs to first detect that the behaviour is triggered, this is often very tricky or expensive. Its easy to define that a program should halt, if it writes outside an array, but detecting if it does can be both slow and hard to implement. There are i…

> How would you optimize: (x * 2) / 2 I'd do the math myself and just write x. I don't even use * for multiplication anymore, I use __builtin_mul_overflow and then check the result. Anyone who doesn't is gonna hit the overflow case one day, and they'll be lucky if their program isn't exploited because of it. I've been making an effort to use all the overflow checking builtins by default in most if not all cases. I've…

The things you want from C isn't C. Id advice you to use another language.
Post reply on HN