Skip variable declaration with goto?

I am reading C programming - a modern approach of K.N. King to learn the C programming language, and it was noted that goto should not skip variable-length array declarations.

But now the question arises: why are goto jumps allowed to skip fixed-length array declarations and regular declarations? And, more precisely, what is the behavior of such examples, in accordance with the C99 standard? When I tested these cases, it seemed that the declarations did not actually jump, but is this true? Are variables whose ads can be intercepted 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]); 
+5
source share
3 answers

I am determined to explain this without detailed memory information (believe me, they are very hot when using VLAs, see @Ulfalizer answer for details).

So, initially on C89 it was mandatory to declare all variables at the beginning of the block, for example:

 { int a = 1; a++; /* ... */ } 

This directly implies a very important thing: one block == one immutable set of variable declarations.

C99 changed that. In it, you can declare variables in any part of the block, but declarations of declarations are still different from ordinary operators.

In fact, to understand this, you can imagine that all variable declarations are implicitly moved to the beginning of the block, where they are declared and inaccessible to all operators that pursue them.

This is simply because one block == another set of ad rules is saved.

That is why you cannot "jump over the declaration." The declared variable will still exist.

The problem is initialization. He does not "move" anywhere. So, technically for your case, the following programs can 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 still exists, what is being skipped is initialization.

The reason this does not work with VLA is because they are different. In short, this is because it really is:

 int size = 7; int test[size]; 

VLA announcements will, unlike all other announcements, behave differently in different parts of the block where they are declared. In fact, a VLA can have completely different memory layouts depending on where it is declared. You simply cannot β€œmove” it outside the place where you just jumped.

You may ask, "Well, then why not make the announcement not affected by goto "? Well, you still get cases like this:

 goto later; int size = 7; int test[size]; later: 

What do you really expect from this? ..

Thus, the prohibition of skipping VLA announcements exists for any reason - this is the most logical solution to consider such cases, simply banning them at all.

+9
source

The reason you are not allowed to skip the declaration of a variable length array (VLA) is because it will be confused with the way VLAs are typically implemented and complicate the semantics of the language.

The VLA implementation method will most likely be implemented in practice, decreasing (or increasing on the architecture where the stack grows up) the stack pointer by the dynamic (calculated at runtime) amount to make room for the VLA in the stack. This happens at the point where the VLA is declared (conceptually, at least ignoring the optimization). This is necessary so that subsequent stack operations (for example, pushing arguments onto the stack to call a function) are not included in VLA memory.

For VLAs nested in blocks, the stack pointer is usually restored at the end of the block containing the VLAs. If a goto were allowed to jump into such a block even after declaring the VLA, then the code to restore the stack pointer would run without the appropriate initialization code, which could cause problems. For example, the stack pointer can be increased by the VLA, even if it never decreased, which, among other things, could make the return address that was pressed when the function containing the VLA called the stack pointer.

It is also erratic in terms of pure semantic semantics. If you are allowed to skip the declaration, then what will be the size of the array? What should sizeof return? What does it mean to access it?

For cases other than VLA, you just skip initializing the values ​​(if any), which does not necessarily cause problems in itself. If you go to a definition other than VLA, for example, int x; , then storage will still be reserved for the variable x . VLAs differ in that their size is computed at runtime, which complicates the situation.

As one of the notes, one of the motivations for declaring variables anywhere in a block in C99 (C89 requires declarations to be at the beginning of the block, although at least GCC allows them inside the block as an extension) was support for OLA. The ability to perform calculations earlier in the block before declaring a VLA size is convenient.

For some reason, C ++ does not allow goto skip object declarations (or initializations for plain old data types, such as int ). This is because it would be unsafe to jump over the code that calls the constructor, but still run the destructor at the end of the block.

+5
source

Using goto to go through a variable declaration is almost certainly a really bad idea, but it is completely legal.

C distinguishes between the lifetime of a variable and its scope.

For a variable declared without the static inside the function, its scope (the text area of ​​the program in which its name is displayed) extends from the definition to the end of the closest closing block. Its service life (storage time) begins at the entrance to the block and ends at the exit from the block. If it has an initializer, it is executed when (and if) a definition is reached.

For instance:

 { /* the lifetime of x and y starts here; memory is allocated for both */ int x = 10; /* the name x is visible from here to the "}" */ int y = 20; /* the name y is visible from here to the "}" */ int vla[y]; /* vla is visible, and its lifetime begins here */ /* ... */ } 

For variable-length arrays (VLAs), the visibility of the identifier is the same, but the lifetime of the object begins with the definition. What for? Since the length of the array is not necessarily known to this point. In this example, it is not possible to allocate memory for vla at the beginning of the block, because we do not yet know the value of y .

A goto that skips the definition of an object bypasses any initializer for that object, but memory is still allocated for it. If goto goes into a block, memory is allocated when the block is entered. If this is not the case (if both the goto tag and the target label are at the same level in the same block), the object will already be selected.

 ... goto LABEL; { int x = 10; LABEL: printf("x = %d\n", x); } 

When the printf statement is executed, x exists and its name is visible, but its initialization was bypassed, so it has an undefined value.

The language forbids a goto skip the definition of an array of variable length. If it were allowed, it would skip the allocation of memory for the object, and any attempt to refer to it will cause undefined behavior.

goto statements have their capabilities . Using them to skip declarations, although permitted by the language, is not one of them.

+3
source

All Articles