Everything in C is undefined behavior
611–620 of 748 posts
Re: Everything in C is undefined behavior
#612The 5 stages of learning about UB in C: -Denial: "I know what signed overflow does on my machine." -Anger: "This compiler is trash! why doesn't it just do what I say!?" -Bargaining: "I'm submitting this proposal to wg14 to fix C..." -Depression: "Can you rely on C code for anything?" -Acceptance: "Just dont write UB."
Re: Everything in C is undefined behavior
#613Earlier quoted context omitted.
> What stage is the "just make the compiler define the undefined" stage? It can be left as implementation defined, which means that the compiler can't simply do arbitrary things, it needs to document what it would do. Take, for example, signed-integer overflow: currently a compiler can simply refuse to emit the code in one spot while emitting it in another spot in the same compilation unit! Making it IB means that th…
Completely agree. It can, and I think it's extremely annoying that it wasn't. So we have the next best thing: builtins and flags. So long as those cover all the undefined behavior there is, we can live with it. Compiler gets to be "conformant" and we get to do useful things without the compiler folding the code into itself and inside out.
Re: Everything in C is undefined behavior
#614Earlier quoted context omitted.
Intuitively yes - the program will be compiled as if B-inputs are never passed to the program, and that can include eliminating code that tries to detect B-inputs.
This is a description of an imaginary compiler, evoked by the ANSI/ISO standards documents, which has never existed and will never exist. To understand what the program will do, you just have to understand the compiler behavior on your target platforms. A helpful intuition pump is: imagine the ANSI/ISO specifications simply do not exist; now what? Well, you just continue your engineering practice, the way you would f…
Signed vs unsigned chars, and the accompanying extension rules, have already bitten me switching between x86/ARM compilers. Confused the hell out of me when I was just starting out with C.
If you're going to interpret C as in "C on amd64, running on Linux 7.0 on an Arrow Lake Intel processor" then yes, you can get away with a lot of UB. That mitigates the problem but doesn't make it go away.
Re: Everything in C is undefined behavior
#615Re: Everything in C is undefined behavior
#616A lot of this stems from trying to insist that char just means "small" and not "8 bits" and that int means "bigger than that" and not "32 bits". In fairness, K&R dealt with an era where 9 bit architectures existed, but char is 8 bits now. Everywhere.
Re: Everything in C is undefined behavior
#617Earlier quoted context omitted.
By MMIO semantics do you mean explicit load and store instructions? I’ve never felt that pointer reads or writes were lacking descriptiveness here. I would argue the only surprising thing is that they might be optimized out (which is what volatile prevents). Volatile on a non pointer value is not for MMIO, though, that’s typically for concurrency like with interrupts.
> I’ve never felt that pointer reads or writes were lacking descriptiveness here. I would argue the only surprising thing is that they might be optimized out The C and C++ languages would be very slow by modern standards if you insist that reading or writing via a pointer must result in immediate fetches or stores to memory. > Volatile on a non pointer value is not for MMIO, though, that’s typically for concurrency l…
Please explain. How would you make the variable backed by a hardware register region? Is this using some sort of linker trick to change where the value lives in memory?
Re: Everything in C is undefined behavior
#618C is still, by far, the simplest language that we have. Although many newer languages are safer (with the exclusion of Rust, primarily by being slower) the same kinds of issues that are there in C are there in these languages, their effects are just harder to see. People complain about C as though they know how to fix it.
It's slower than Fortran and, depending on the platform, cobol. It's a bigger minefield than any language that came after it barring C++.
The only real advantage I can ascribe to C is that it's actually still being used after all these years, and it mostly works similarly on most hardware, like a Java for people who enjoy the casino.
Fixing C without breaking existing C code is pretty much impossible. You can start by defining warnings for UB, but then you will break any of the more trivial examples in the article. You can also start by simply killing off weird platforms (force a specific amount of bits for instance, screw the weird 16 bit char chips). Making casts explicit would probably fix a lot of problems too, though you'd need better syntax for that.
There is no fixing C without changing what C really is.
Re: Everything in C is undefined behavior
#619Earlier quoted context omitted.
There is an important distinction here to the technical meaning of UB that is lost to many. UB simply means the operation you are intending to perform has no defined semantic under the ISO C specification. That is all. Understand what this means but do not read further into it. It is easy to read further into this as you have and many do, and come to incorrect conclusions, and think this MUST result in incorrect beha…
Maybe I'm misunderstanding. Here is what I'm trying to say. "Accessing an object which is not correctly aligned" - this is UB "As an example of this, take this code: ..." - this (code) is not UB. Is this incorrect somehow? You could interpret the second sentence as 'under the assumption of an unaligned pointer, let's look at what this seemingly innocuous (and correct) code does.' But that's not what they did. They pr…
You are beginning to understand. Yes, surprisingly, it is (1) that is being claimed.
The mere expression is alone UB. Yes, you read that right. In source code, it's already UB. Why? Because the ISO spec defined UB that way. But you see, what this means in practice ie whether "it works" is an entirely separate question and would be specific to toolchain, hardware, runtime, the alignment of the pointer in question, blah blah.
There is nuance here, and that's why this topic is debated to death, because it's hard to explain and it is genuinely complex.
When people say something is UB, they mean to say that the behaviour is undefined--wrt to ISO C.
The behaviour that actually matters IS defined wrt toolchain, hardware, runtime, alignment of pointer in question.
But that's exactly it--the latter is not what we mean when we say something is UB, when we say something is UB we are talking about the ISO C spec. The important follow up question then, when knowingly invoking UB, is to ensure your environment is "correct", because you have now crossed into realms entirely out of the auspices of the ISO C spec. Ergo, you are now in UB land; what you thought was the foundation of your codebase, the ISO C spec, has now turned into quicksand.
It is this implied undocumented dependence on factors external to the source code that is a huge source of bugs and surprisal.
So take this example from the article. Yes, it is UB by construction.
(edit: i copied the wrong fragment initially -- if you were talking about the int foo(const int* p) fragment, yes that block is not by construction UB)
bool parse_packet(const uint8_t\* bytes) {
const int\* magic_intp = (const int*)bytes; // UB!
int magic_raw = foo(magic_intp); // Probably crashes on SPARC.
int magic = ntohl(magic_raw); // this is fine, at least.
[…]
}
Why?> Because the compiler is not obligated to generate assembly instructions that work on unaligned pointers. Because it’s UB.
Does it actually work though? It might and it might not: there is simply no guarantee from the language. But that's all it says. It may very well work on your arch and platform and toolchain, indefinitely. But again circling back, for code written like this to be so brittle, that is why UB is to be avoided.
And to your point:
> that's where the debate should be, not illusorily ascribed to derefing pointers.
But that is where the debate is. People just do not understand what UB actually means. The article is correct: everything in C is UB. The takeaway is not that, therefore all C code is irredeemably broken (well, to some people it does mean that, anywho..). The takeaway is that most C code IS in fact more delicate than one may originally believe, because of the fact ISO C is under-specified, to allow for specialization dependent on toolchain/arch/hardware/what have you etc.
So it is incumbent on the developer when writing C to correctly acknowledge when they are invoking UB, and to do so intentionally with the awareness that things may just randomly break one day.