I am reading C Programming - A Modern Approach by K.N.King to learn the C programming language and it was noted that goto
statements must not skip variable-length array declarations.
But now the question is: Why are goto
jumps allowed to skip fixed-length array declarations and ordinary declarations? And more precisely, what is the behavior of examples like these, according to the C99 standard? When I tested these cases it seemed like the declarations were actually not jumped over, but is that correct? Are the variables whose declarations might have been jumped over safe to use?
1.
goto later;
int a = 4;
later:
printf("%d", a);
2.
goto later;
int a;
later:
a = 4;
printf("%d", a);
3.
goto later;
int a[4];
a[0] = 1;
later:
a[1] = 2;
for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
printf("%d\n", a[i]);
Answer
I'm in the mood for explaining this without gory memory-layout details (believe me, they get very gory when VLAs are used; see @Ulfalizer's answer for details).
So, originally, in C89, it was mandatory to declare all variables at the start of a block, like this:
{
int a = 1;
a++;
/* ... */
}
This directly implies a very important thing: one block == one unchanging set of variable declarations.
C99 changed this. In it, you can declare variables in any part of the block, but declaration statements are still different from regular statements.
In fact, to understand this, you can imagine that all variable declarations are implicitly moved to the start of the block where they are declared and made unavailable for all statements that preceed them.
That is simply because the one block == one set of declarations rule still holds.
That is why you cannot "jump over a declaration". The declared variable would still exist.
The problem is initialization. It doesn't get "moved" anywhere. So, technically, for your case, the following programs could be considered equivalent:
goto later;
int a = 100;
later:
printf("%d", a);
and
int a;
goto later;
a = 100;
later:
printf("%d", a);
As you can see, the declaration is still there, what is being skipped is initialization.
The reason this doesn't work with VLAs is that they're different. In short, it's because this is valid:
int size = 7;
int test[size];
The declarations of VLAs will, unlike all other declarations, behave differently in different parts of the block where they are declared. In fact, a VLA might have entirely different memory layouts depending on where it is declared. You just can't "move" it outside of the place you just jumped over.
You may ask, "all right, then why not make it so that the declaration would be unaffected by the goto
"? Well, you'd still get cases like this:
goto later;
int size = 7;
int test[size];
later:
What do you actually expect this to do?..
So, prohibiting jumping over VLA declarations is there for a reason - it is the most logical decision for dealing with cases like the above by simply prohibiting them altogether.
No comments:
Post a Comment