How can you identify legal C without understanding undefined behavior? Or rather, what is the list of UB that nay professional C developer should be expected to know?
> If someone has some examples that a reasonable person would write, specifically examples that look like the simplest way to do something, then I’d love to hear them.
Basically all OOP-style C. For instance, the Windows macro
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
The Linux kernel is also an excellent source of these.Also, serialization/deserialization libraries are an excellent source for UB. And huge amounts of code depends on UB by using unsigned char arrays for storage of other types.
Furthermore, in practice, C code assumes most of the following:
- null pointers are represented as 0, e.g. if(ptr).
- pointer provenance does not exist
- char and unsigned char are 8-bit bytes
- two's-complement arithmetic
- page-based memory protection
- no types have trap representations
- the values of base types, besides floating point, do not have multiple representations.
- etc
Off the top of my head, as a non professional C programmer:
- can’t read from uninitialized memory
- one malloc = one free
- signed overflow is undefined
- pointers to stack variables can’t outlive the stack variable itself
- can’t dereference NULL pointers
(Obviously take this with a massive grain of salt, there are almost certainly important ones that I’m forgetting.)
> null pointers are represented as 0, e.g. if(ptr).
If(ptr) is guaranteed by the standard to work. Memset(ptr, 0, len) is not (and is very broken in other ways as well).
> char and unsigned char are 8-bit bytes
They’re guaranteed to be one byte, where a byte is at least 8 bits. So unless you’re trying to simulate %256 with an implicit conversion, or relying on unsigned overflow to turn 0xFF into 0x00, then you’ll be fine.
And I would never excrete such a line of code without automatically thinking "hmmm.. does this really do what I think it does?"
This is an undefined operation in almost all programming languages.
There is no intrinsic reason why a[i] = i++ has to result in undefined behavior.
I think the second example may contain UB on a <=32-bit architecture (right shift by a value greater or equal to the number of bits), or at least this is UB in C++. On a 64-bit architecture it would be fine (but the result would not be 0).
if i is signed, this is undefined. You'd think it's just a loop though.
while (1) for(;;)int this_is_a_fairly_long_variable_name_1;
int this_is_a_fairly_long_variable_name_2;
Why? Because section 5.2.4.1 requires implementations to support at least 32 significant characters in external identifiers and section 6.4.2.1 says "If two identifiers differ only in nonsignificant characters, the behavior is undefined."
These ones may be more "obvious", but:
Given e.g. int a[4][5] then accessing a[1][7] is undefined even though one might assume it's like accessing a[2][2]
x << -3 is undefined behaviour though logically one might assume it should be equivalent to x >> 3
And the classic, very common newbie use of "fflush(stdin);" is also undefined.
Not saying anything about the other things, but this (in my opinion) is something that should raise eyebrows. At least to me personally that looks rather weird.
(It's also not unique to C)
It's not obvious which actions are atomic.
There’s no need to have any subset of the language in memory, this is the whole standard.
No, you're wrong. "Undefined Behavior" (UB) in the context of C has a very different meaning from the colloquial meaning "not specified". The meaning of UB is explicitly defined in the C standard, and the C standard explicitly lists certain program execution patterns as UB.
> If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe ‘‘behavior that is undefined’’