In C one can't have a labeled variable declaration. This quirk probably appeared in C99. Before that variable declarations had to be at the beginnings of blocks, so there could never be a variable declaration after a label. C99 allowed intermixing variable declarations and statements, but the grammar remained the same, allowing only statements after labels.
cases
$ gcc --version
gcc (Alpine 13.2.1_git20240309) 13.2.1 20240309
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
a.c
:
#include <stdlib.h>
int main(void)
{
switch (42) {
case 1: int a; (void) a; break;
case 2: break;
}
return EXIT_SUCCESS;
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
a.c
a.c: In function 'main':
a.c:6:17: warning: a label can only be part of a statement and a declaration is not a statement [-Wpedantic]
6 | case 1: int a; (void) a; break;
| ^~~
b.c
:
#include <stdlib.h>
int main(void)
{
switch (42) {
case 1: { int a; (void) a; break; }
case 2: break;
}
return EXIT_SUCCESS;
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
b.c
c.c
:
#include <stdlib.h>
int main(void)
{
switch (42) {
case 1:; int a; (void) a; break;
case 2: break;
}
return EXIT_SUCCESS;
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
c.c
d.c
:
#include <stdlib.h>
int main(void)
{
goto a;
a:
int a;
(void) a;
return EXIT_SUCCESS;
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
d.c
d.c: In function 'main':
d.c:7:5: warning: a label can only be part of a statement and a declaration is not a statement [-Wpedantic]
7 | int a;
| ^~~
e.c
:
#include <stdlib.h>
int main(void)
{
goto a;
a:
{ int a;
(void) a; }
return EXIT_SUCCESS;
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
e.c
f.c
:
#include <stdlib.h>
int main(void)
{
goto a;
a:;
int a;
(void) a;
return EXIT_SUCCESS;
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
f.c
In C++ jumping into a scope of a variable that was declared with an initializer is not allowed (ISO C++ '03 6.7/3, page 100, i.e. 128). Non-POD variables are initialized implicitly (if there's no explicit initializer), as such one can't ever jump over non-POD declarations. But one can jump over POD declarations without an initializer:
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps 77) from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer (8.5).
Supposedly they wanted to keep some compatibility with C (in C one can jump over variabile declarations without initialization), but didn't want to let one have uninitialized objects (without their constructor being called). Especially considering that constructors can be called implicitly. Probably because that's an extra guarantee and it's somewhat easier if you know that an object is always initialized (if you have access to an object, its constructor was called).
More quotes here;
cases
a.cpp
:
int main()
{
switch (42) {
case 1: int a = 1; (void) a; break;
case 2: break;
}
}
$ gcc -Wall -Wextra -pedantic -O a.cpp
a.cpp: In function 'int main()':
a.cpp:5:14: error: jump to case label
5 | case 2: break;
| ^
a.cpp:4:21: note: crosses initialization of 'int a'
4 | case 1: int a = 1; (void) a; break;
| ^
b.cpp
:
int main()
{
switch (42) {
case 1: { int a = 1; (void) a; break; }
case 2: break;
}
}
$ gcc -Wall -Wextra -pedantic -O b.cpp
c.cpp
:
int main()
{
switch (42) {
case 1: int a; a = 1; (void) a; break;
case 2: break;
}
}
$ gcc -Wall -Wextra -pedantic -O c.cpp
d.cpp
:
int main()
{
goto a;
int a = 1;
(void) a;
a:
return 0;
}
$ gcc -Wall -Wextra -pedantic -O d.cpp
d.cpp: In function 'int main()':
d.cpp:6:1: error: jump to label 'a'
6 | a:
| ^
d.cpp:3:10: note: from here
3 | goto a;
| ^
d.cpp:4:9: note: crosses initialization of 'int a'
4 | int a = 1;
| ^
e.cpp
:
int main()
{
goto a;
{ int a = 1; (void) a; }
a:
return 0;
}
$ gcc -Wall -Wextra -pedantic -O e.cpp
f.cpp
:
int main()
{
goto a;
int a;
a = 1;
(void) a;
a:
return 0;
}
$ gcc -Wall -Wextra -pedantic -O f.cpp