I tried to find all those claims in the C99 standard.
You won't find them there. These are implementation details. They are not explicitly covered by the standard. The standard describes the required behavior. How that behavior is achieved in specific implementations is a different story.
What C standard requires, for one example, is the following assertion to hold
int n = 10;
typedef char A[n++ / 2];
n += 42;
assert(sizeof(A) == 5);
and that immediately implies that in general case the compiler will have to store the array size somewhere in a hidden variable. The compiler will have to do it at the point where control passes over the typedef. The standard does not have to say it explicitly, but there's simply no other way around it in general case.
That's why I call that a
run-time typedef. It generates code and it occupies storage.
All I could really find is that the *size of the array* has to be evaluated when the typedef is encountered. This is this evaluation that could lead to actual code being emitted for it, as far as I understand.
No, they key point here is not the evaluation per se. It is the fact that the evaluated value has to be "frozen" at that very moment. The original expression cannot be reevaluated every time that size is requested by the subsequent code (e.g. by a `sizeof`), since the components of that expression might be lost already. For which reason the implementations are forced to evaluate only once and store the result in a hidden variable.
You can easily see that hidden variable in Godbolt:
int main()
{
int n1 = 10, n2 = 20;
typedef char A[n1 + n2];
}
main:
push rbp
mov rbp, rsp
push rbx
mov DWORD PTR [rbp-20], 10
mov DWORD PTR [rbp-24], 20
mov edx, DWORD PTR [rbp-20]
mov eax, DWORD PTR [rbp-24]
add eax, edx
movsx rdx, eax
sub rdx, 1
mov QWORD PTR [rbp-32], rdx
cdqe
mov rcx, rax
mov ebx, 0
mov eax, 0
mov rbx, QWORD PTR [rbp-8]
leave
ret
https://godbolt.org/z/G7e7GW7Y7The `mov QWORD PTR [rbp-32], rdx` is exactly that - initialization of a hidden variable to the current value of `n1 + n2` (this implementation actually stores `n1 + n2 - 1`, if you notice).
Initialization of that hidden variable is the reason why the typedef cannot be skipped. If you somehow manage to skip it, `sizeof(A)` will access a garbage value from that hidden variable, which is what happens in TinyC actually (TinyC might pre-initialize it to zero, but that's beside the point).
Now until you actually declare a variable of that type, no space should be allocated for it.
... except for that hidden variable. It can be optimized away in many cases or replaced by a CPU register, but not always, of course.
But even if it forces evaluation of the size - even if you don't use the type - this piece of code should be pretty harmless?
If you don't "use" that type in any way, then yes, it is probably harmless from practical point of view... But who cares about such contrived cases in practice? And the standard prohibits skipping.
Normally, if you declared it, you will most certainly use it. And if you somehow manage to skip the typedef, all hell will break loose once you attempt to use the type. E.g. `sizeof(A)` will evaluate to a garbage value. This is not harmless at all.
If you know GCC well enough, you know how to force the skip: a GCC extension - run-time goto - will help you. Try this
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n = 10;
if (rand() >= 0) /* <- just to suppress optimizations */
goto *&&skip;
typedef char A[n];
skip:;
printf("%zu\n", sizeof(A));
}
https://godbolt.org/z/bs8PMafhGThe reason it prints garbage is because it retrieves the size from an uninitialized location. (It retrieves it from a register in this example, but it is beside the point).
Now don't hesitate to point us to the exact parts of the standard that would back up all your claims, with the associated consequences.
Well, again, see the above. You have already found, I believe, the parts of the standard that prohibit the jump. The rest is out of the standard's scope.
Of course, this is a rather abstract and pedantic way to view of the matter. When the standard features are developed and discussed, possible implementation approaches are always taken into account. Normally, standard will never require something that it does not know how to implement. In many (or most) cases the standard usually implicitly targets one specific approach to the implementation. My description above is the implementation the standard had in mind. Think of me as the proverbial "horse's mouth" when it comes to matters like that.