Also, my original example is invalid C++, as is yours.
C++ does not allow goto to skip variable initializations.
In C++, if you want to goto past some variable declarations, you have to do the right thing and encapsulate them into a block.
GCC's diagnostics look like:
goto.cc: In function ‘void f()’:
goto.cc:7:1: error: jump to label ‘end’ [-fpermissive]
end:;
^~~
goto.cc:3:8: note: from here
goto end;
^~~
goto.cc:5:7: note: crosses initialization of ‘int x’
int x = 42;
^I see. I think I get what you're saying: if you only put variable declarations at the top of the scope, then if you only jump to labels at the same statement depth as the goto statement, you won't skip initialization or assignment of uninitialized variables. Whereas, if you mix declarations and statements, you might end up with uninitialized variables even when you only jump to labels at the same statement depth as the goto statement. Is that what you're saying, or did I grasp something orthogonal to your point?
> C++ does not allow goto to skip variable initializations.
I didn't know that until now. However, if you change the function so that x is assigned after being declared, it still compiles and produces the same result.
void c89_fn(int arg) {
if (arg > 0)
goto after;
{
int x;
x = 42;
after:
/* x is in scope, but x could be uninitialized */
printf("%d\n", x);
}
}
That's true when you write a goto that goes around scopes, and doesn't jump into them. However, you can still jump into the middle of an inner scope, where sometimes a variable might be uninitialized.
For this particular example, GCC and Clang inline the definition of x as 42 unconditionally with any amount of optimizations on, but with -O0, both generate code that reads from a sometimes-uninitialized stack address. There's probably a more practical example that one could find.