After my post on undefined behavior in C some people insisted that it’s demonstrably fine, we know what the code will do, and at worst we’ll get a segfault.
An implementation is allowed to provide a definition for what the C standard leaves implicitly or explicitly undefined. There are also standards, such as POSIX, that themselves define some things which standard C leaves undefined.
This is not to say that all UB is like this, or that all people who write C and introduce UB into their code know what they’re doing, but some people do know what they’re doing.
If you are writing C and you are writing C against one of these other standards, or specifically against a specific implementation, there is nothing wrong with relying on the specific explicit definitions of behaviour which may otherwise be undefined, as long as you’re doing it consciously.
(I maintain, however, that nobody that is looking for advice on whether they should write C should not be writing C. The people who are writing C and are not looking or advice on whether they should continue to write it should also seriously consider moving to something with fewer footguns if they can.)
There are also standards, such as POSIX, that themselves define some things which standard C leaves undefined.
And that does not matter.
I C says an API is UB if used in a specific way, it does not matter what the API implementation does or says. If the C standard says that is UB, it puts no requirements on the compiler.
There is no difference between using an API that posix says is defined but C says is not vs dividing by zero, or overflowing an integer.
If you write C code that depends on implementation defined behavior (POSIX), but C says is UB, your C code is wrong.
The fact that you can get away with it today, doesn’t magically make your code correct. It does not mean that it will work if you change header orders, or inlining changes, or anything else.
If your code invokes UB according to the language, you are not writing conforming C. Maybe your compiler allows it (for example -fwrapv, -ftrapv, -fno-delete-null-pointer-checks, etc) but that is no longer conforming C, and your program is wrong vs the standard: any other C compiler is free to assume your UB code does not occur, or is unreachable, etc.
What elevates the C standard to this special status above other standards such as POSIX and other documentation?
In my opinion, it’s nothing. If you’re targeting the C standard, its rules apply. If you’re targeting POSIX, you get to rely on its guarantees. If you’re targeting glibc, you get to rely on its guarantees.
The C standard is not the law. Heck, it’s not even a certification. C compilers can remain noncompliant for decades and face no consequences at all.
It isn’t special. C has its own defined language semantics.
POSIX can have its own.
If you want to use POSIX APIs in accordance to the POSIX standard, even if that conflicts with C, you just need to accept you are (1) not programming in C, and (2) if hoping the compiler is choosing to not apply C semantics.
If you want to use POSIX APIs in accordance to the POSIX standard, even if that conflicts with C, you just need to accept you are (1) not programming in C, and (2) if hoping the compiler is choosing to not apply C semantics.
This is crossing into genuine nonsense territory.
Nowhere in the “C semantics” does it say that a compiler must create nasal demons for every instance of UB being invoked. POSIX semantics do not “conflict with C” when they define some specific behavior that’s been left undefined by ISO C.
If I’m using POSIX APIs in accordance with the POSIX standard, and using a C toolchain that implements said POSIX standard, I do not need to hope for anything, and the toolchain has no freedom to choose: if the behavior of a particular operation is undefined in ISO C, but is very much defined by POSIX, then POSIX wins. Same with everything else.
In no way my reply above could be interpreted as you’ve just interpreted it.
It seems that you are trying to twist every possible consideration into something to support your position, even when it is clearly nonsensical. Please stop doing that.
You literally started your comment with an accusation of me stating genuine nonsense, so please don’t accuse me of bad faith. Especially when you followed with this:
If I’m using POSIX APIs in accordance with the POSIX standard, and using a C toolchain that implements said POSIX standard, I do not need to hope for anything, and the toolchain has no freedom to choose: if the behavior of a particular operation is undefined in ISO C, but is very much defined by POSIX, then POSIX wins. Same with everything else.
If C also defines a method, which for many “POSIX” APIs it does, then the C standard definitions of those APIs (things like malloc, realloc, free, etc) are determined by the C standard. POSIX is merely an implementation of those C interfaces, the provides its own extensions to the C interface. Unfortunately instead of those extensions being governed by implementation defined behavior (POSIX definition wins), they’re labeled UB in the standard (POSIX definition is irrelevant).
Anyway. I realize comments like this keep pulling me into this nonsense thread where people seem hell bent on acting like UB is safe if the implementation provides defined behavior, when literally the entire point of my first comment is that every single one of the expectations that people present is instantly resolved by the standard saying something is implementation defined, not UB.
Undefined behavior gives the implementor license not to catch certain program errors that are difficult to diagnose. It also identifies areas of possible conforming language extension: the implementor may augment the language by providing a definition of the officially undefined behavior.
POSIX extends C by providing a definition of some officially undefined behavior.
they’re labeled UB in the standard (POSIX definition is irrelevant).
No, UB is not required to be undefined: one of its purposes is to provide extension points for implementations. See my earlier quotes from the rationale.
You won’t find this out by reading the standard, but (as well as the rationale) it’s often mentioned in committee papers.
An implementation is allowed to provide a definition for what the C standard leaves implicitly or explicitly undefined. There are also standards, such as POSIX, that themselves define some things which standard C leaves undefined.
This is not to say that all UB is like this, or that all people who write C and introduce UB into their code know what they’re doing, but some people do know what they’re doing.
If you are writing C and you are writing C against one of these other standards, or specifically against a specific implementation, there is nothing wrong with relying on the specific explicit definitions of behaviour which may otherwise be undefined, as long as you’re doing it consciously.
(I maintain, however, that nobody that is looking for advice on whether they should write C should not be writing C. The people who are writing C and are not looking or advice on whether they should continue to write it should also seriously consider moving to something with fewer footguns if they can.)
score 1 · canonical
You cannot write code that depends on UB.
There are also standards, such as POSIX, that themselves define some things which standard C leaves undefined.
And that does not matter.
I C says an API is UB if used in a specific way, it does not matter what the API implementation does or says. If the C standard says that is UB, it puts no requirements on the compiler.
There is no difference between using an API that posix says is defined but C says is not vs dividing by zero, or overflowing an integer.
If you write C code that depends on implementation defined behavior (POSIX), but C says is UB, your C code is wrong.
The fact that you can get away with it today, doesn’t magically make your code correct. It does not mean that it will work if you change header orders, or inlining changes, or anything else.
If your code invokes UB according to the language, you are not writing conforming C. Maybe your compiler allows it (for example -fwrapv, -ftrapv, -fno-delete-null-pointer-checks, etc) but that is no longer conforming C, and your program is wrong vs the standard: any other C compiler is free to assume your UB code does not occur, or is unreachable, etc.
score 1 · canonical
What elevates the C standard to this special status above other standards such as POSIX and other documentation?
In my opinion, it’s nothing. If you’re targeting the C standard, its rules apply. If you’re targeting POSIX, you get to rely on its guarantees. If you’re targeting glibc, you get to rely on its guarantees.
The C standard is not the law. Heck, it’s not even a certification. C compilers can remain noncompliant for decades and face no consequences at all.
score 2 · canonical
It isn’t special. C has its own defined language semantics.
POSIX can have its own.
If you want to use POSIX APIs in accordance to the POSIX standard, even if that conflicts with C, you just need to accept you are (1) not programming in C, and (2) if hoping the compiler is choosing to not apply C semantics.
score 1 · canonical
This is crossing into genuine nonsense territory.
Nowhere in the “C semantics” does it say that a compiler must create nasal demons for every instance of UB being invoked. POSIX semantics do not “conflict with C” when they define some specific behavior that’s been left undefined by ISO C.
If I’m using POSIX APIs in accordance with the POSIX standard, and using a C toolchain that implements said POSIX standard, I do not need to hope for anything, and the toolchain has no freedom to choose: if the behavior of a particular operation is undefined in ISO C, but is very much defined by POSIX, then POSIX wins. Same with everything else.
score 1 · canonical
My god you’re right, that’s why we don’t need to worry about compilers removing null checks, or pretending integer overflow doesn’t occur, right?
score 1 · canonical
In no way my reply above could be interpreted as you’ve just interpreted it.
It seems that you are trying to twist every possible consideration into something to support your position, even when it is clearly nonsensical. Please stop doing that.
score 1 · canonical
You literally started your comment with an accusation of me stating genuine nonsense, so please don’t accuse me of bad faith. Especially when you followed with this:
If C also defines a method, which for many “POSIX” APIs it does, then the C standard definitions of those APIs (things like malloc, realloc, free, etc) are determined by the C standard. POSIX is merely an implementation of those C interfaces, the provides its own extensions to the C interface. Unfortunately instead of those extensions being governed by implementation defined behavior (POSIX definition wins), they’re labeled UB in the standard (POSIX definition is irrelevant).
Anyway. I realize comments like this keep pulling me into this nonsense thread where people seem hell bent on acting like UB is safe if the implementation provides defined behavior, when literally the entire point of my first comment is that every single one of the expectations that people present is instantly resolved by the standard saying something is implementation defined, not UB.
So I’m good.
score 0 · canonical
POSIX extends C by providing a definition of some officially undefined behavior.
score 1 · canonical
No, UB is not required to be undefined: one of its purposes is to provide extension points for implementations. See my earlier quotes from the rationale.
You won’t find this out by reading the standard, but (as well as the rationale) it’s often mentioned in committee papers.
score 2 · canonical