While writing some code for a code golf competition, I noticed some strange behavior. For example:
int main(void)
{
goto jmp;
char *str = "Hello, World!";
jmp:
puts(str);
}
Compiling with GCC (and Clang and MSVC) results in no warnings or errors, but running it throws SIGSEGV
. How come the compilers don't catch that the goto
is jumping around the variable declaration?
I decided to test this (bug?) out, and rewrote the example:
int main(void)
{
goto jmp;
int x;
jmp:
putchar(x);
}
Again, compilation yields no errors. Furthermore, nothing is thrown upon execution, yet in MSVC the process exits with a non-zero exit code.
What's going on here? Is this simply another reason we shouldn't use goto
s? And how come no errors are thrown in the second example, while SIGSEGV
is thrown in the first?
It's permitted to jump past initialization of a local variable, the effect will be that the variable is uninitialized.
Passing an uninitialized variable to puts
is undefined behaviour, but not a constraint violation nor a syntax error. This means the C Standard does not require the compiler to give an error.
However, compilers are thoughtful and tend to supply various warning flags. In this case , gcc can warn about potential use of uninitialized variables. By using -Wall
, or -Wuninitialized
, you should see a warning. You can use -Werror
or -Werror=uninitialized
to get an error instead of a warning.
Some people advise to always compile in standard mode with warnings, e.g. -std=c11 -pedantic -Wall -Wextra
.
Regarding "in MSVC the process exits with a non-zero exit code." , the MSVC compiler is only up to the C89 standard, in which falling off the end of main
without returning a value, returns garbage. You should have return 0;
at the end of main
if needing to support ancient compilers like that.