Then how can I check for it?
Depends on operation. I showed example above for overflow check when adding of int32_t types - cast to int64_t and check if result is in bounds (need to check against max negative number for underflow to). It's just a bit of extra math. If you don't want to cast, then it's extra comparisons:
int add_safe(int a, int b)
{
if (a > 0 && b > INT_MAX - a) { ERROR: overflow happens }
else if (a < 0 && b < INT_MIN - a) { ERROR: underflow happens }
return a + b; // now it is OK
}
What does sign or zero-extended mean?
Sign extended means taking top bit and replicating it on all the newly prepended top bits. Zero extended means setting them always to 0. So casting uint8_t -> int32_t puts 0 on top 24 bits. Casting int8_t -> int32_t puts 8th bit of int8_t in top 24 bits.
What about signed types? And what does implementation-defined mean?
For signed types it is implementation defined. Implementation defined means that behavior depends on target architecture and/or compiler. Meaning if you know what it does for your arch/compiler then you can use it and it is fine - it is allowed operation (vs UB which is never allowed). If it does something different from what you want, then you don't use it, you do the operation differently.
Points 5, 6, and 7 are hard for me to understand. Another comment also mentioned strict aliasing. Can you go a little deeper there? Also, I remember there's a way to turn off strict aliasing through compiler flags.
Are you asking about from C spec point of view? Or from practical point of view? Because in practice in any modern OS the real compilers will do things just fine - you can do arithmetic on pointers regardless where pointers come from or what's their alignment.
I assume when you said "array of 10 chars" you meant a static array char arr[10] where the compiler knows the size. What about malloc/VirtualAlloc
It applies also to malloc'ed arrays too. For VirtualAlloc it does not apply, because C standard does not know anything about such function or what is "virtual memory". In practice it will work just fine, but not from C standard point of view.
or just using the pointer inside a function where the compiler doesn't know the array size or the allocated range?
Compiler does not need to know array size or allocated range. It is up to you to guarantee that.
What does "not from same object' and "unspecified" mean?
int arr1[10];
int arr2[10];
&arr1[4] == &arr1[5] - this is comparison from "same object"
&arr1[4] == &arr2[5] - this is comparison NOT from same object
Similar rule applies to comparing address of struct members.
Unspecified means that result value is not set to anything meaningful (from spec point of view).
So I can't cast between a pointer to a float and a pointer to a double, because one has a 4-byte alignment while the other has 8.
Technically yes. In practice on any modern OS you can do it just fine.
What does it mean?
Trap representation in spec language means that it is sometimes UB to access such value.
It's hard for me to visualize it, can you give me an example?
struct Base { int value; };
struct Extra1 { int value; int value2; };
struct Extra2 { Base base; int value2; };
struct Extra3 { float x; }
Both Extra2 and Extra1 starts with same types as Base structure. But not Extra3.