Consider the following C program
#include <stdio.h>
typedef struct s {
int x;
} s_t;
int main() {
int x;
s_t a;
scanf("%d", &x);
if (x > 0) {
s_t a;
a.x = x;
}
printf("%d\n", a.x);
}
The a struct variable in the if branch clearly shadows the a struct variable in main. One would expect that the output in printf would be undefined, but with GCC the scoped variable seems to equal the main variable.
For example
gcc test.c -o test
echo 10 | ./test
will output 10.
On the other hand, running this through clang, does as expected
clang test.c -o test
echo 10 | ./test
outputs -2145248048.
Is this a GCC bug or is there some sort of undefined behaviour that this is triggering?
gcc 4.8.2
clang 3.4
As others mentioned, you're reading an uninitialized local variable and that's undefined. So, anything is legit. Having said that, there is a particular reason for this behavior: gcc reuses variables on the stack as much as it can (i.e., as long as the generated code is provably correct). You can fine-tune using the -fstack-reuse option.
To disable stack reuse:
gcc test.c -o test -fstack-reuse=none
echo 10 | ./test
4195808 # prints some random number.
To enable stack reuse for all variables:
gcc test.c -o test -fstack-reuse=all #, or -fstack-reuse=named_vars
echo 10 | ./test
10 # prints 10, as it reuses the space on the stack.
This is fully documented on GCC Code Generation Options.
One would expect that the output in printf would be undefined
It is undefined. Undefined behaviour means that anything can happen, including (but not limited to) any particular output.
In this case what's likely happening is that the compiler optimizes both a to have the same address.
No compiler bug, your program invokes undefined behavior.
You are reading an uninitialized automatic object and C says it has indeterminate value and that reading it is undefined behavior.
Give a.x (the one declared in main scope) a value to give your program a specified behavior.
Is this a GCC bug or is there some sort of undefined behavior that this is triggering?
It's undefined behavior. s_t a; in the if statement hides the previous declaration of a.
In the block
if (x > 0) {
s_t a;
a.x = x;
}
x is assigned to the local a.x. After this block, a is no longer available and in printf you are accessing an uninitialized variable. Uninitialized variables may invoke UB.
Related
Consider the following C code:
#include <stdio.h>
int x = 5;
int y = x-x+10;
int z = x*0+5;
int main()
{
printf("%d\n", y);
printf("%d\n", z);
return 0;
}
The ANSI C90 standard states "All the expressions for an object that has static storage duration [...] shall be constant expressions" (6.5.7 constraint 3).
Clearly the initializers for y and z are not constant expressions. And indeed, trying to compile the above C code with clang main.c or clang -ansi main.c gives an error for this reason.
However, compiling with gcc main.c or even gcc main.c -ansi -pedantic -Wextra -Wall gives no errors at all, and runs, printing 10 and 5.
On the other hand, trying something like the following:
#include <stdio.h>
int x = 5;
int main()
{
int y[x-x+2];
printf("%lu\n", sizeof(y));
return 0;
}
gives a warning when compiled with gcc -ansi -pedantic ... or clang -ansi -pedantic ....
So gcc randomly performs the mathematically correct cancellations in order to pretend that something is a constant expression, even when asked not to (-ansi). Why is this? Is this a bug?
By the way, my gcc version is 9.4.0 and my clang version is 10.0.0-4.
Without looking, there's one simple explanation: the code that checks whether the initializer is an acceptable constant expression operates on an internal representation after a pass that does arithmetic simplification, so it never "sees" your x-x or x*0.
This probably makes the implementation of the rule in GCC simpler: it just has to ask "is this node one that represents a constant?" rather than "is this tree one that I could evaluate as a constant later on?". It also facilitates the behavior that they probably want for the later standards (see below), and a special case for -ansi would probably add an undesirable amount of code complexity.
Is it a bug? Arguably. But it's one with such a small impact that it's not especially likely to get fixed. It works correctly for valid code, and it errors correctly on "really" invalid code that could actually cause a problem. It only deviates from the standard in a fairly harmless way, and only for C90 (since the C99 and later standards say "an implementation may accept other forms of constant expressions", which gives GCC latitude to allow an expression that mentions things not on the laundry list, as long as it has a constant value).
Hitting below warning with new gcc version 6.X
Warning: 'temp' may be used uninitialized in this function [-Wmaybe-uninitialized]
Code:-
int temp;
if (logcat (MSPRO_P->regs[test],
byte, &temp, test) == FALSE){
memErrorMsgHistoryVa (MSPRO_MEMP, "Invalid Data Count 0 value");
MSPRO_P->flashCmdError = TRUE;
}
gcc isn't supposed to warn about passing a pointer to an uninitialized variable to a function it doesn't know anything about (the assumption is that the function will initialize it). So I'm pretty sure that gcc knows things about logcat and the uninitialized use is detected in there. Maybe it got inlined or such.
Example:
$ cat > foo.c
static int
bar(int *a)
{
return *a + *a;
}
int
foo(void)
{
int x;
int y = bar(&x);
return x + y;
}
$ cc -Wall -c foo.c
$
Here, despite it being blindingly obvious to humans, gcc doesn't actually know what happens inside the function bar. So no warning.
Let's help gcc to understand what's going on:
$ cc -O1 -Wall -c foo.c
foo.c: In function ‘foo’:
foo.c:4:12: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
return *a + *a;
~~~^~~~
foo.c:10:6: note: ‘x’ was declared here
int x;
^
$
Just turning on optimization helped gcc to see what's going on (probably some inlining happened).
From the minimal piece of code you've shown and the warning message, where it looks like you cut out the bit that actually tells you exactly where in your code the problem happens, I conclude that the problem is in your logcat function.
temp is uninitialized after int temp;.
logcat (MSPRO_P->regs[test], byte, &temp, test)
Since a pointer to temp is passed to the function, we, as programmers can guess that this function is supposed to initialize temp. But that is very difficult, if not impossible, for the compiler to assert with absolute certainity, specially when that function is in separate translation unit. From compilers perspective, there is no easy way to tell whether logcat will write to *temp or read it first in uninitialized state. And that's why the warning.
The easiest way to get rid of this warning is assign some initial value to temp, like:
int temp = 0
Okay, so this is a stripped down variant of a bug I had. The bug was that I initialized an array using a variable that wasn't initialized. Earlier I used a function to declare the number of elements using a function, but after a cleanup I forgot about it and moved all declarations to the top of the function.
I used the flags -std=c99 -Wall -Wextra -pedantic -O, and usually gcc warns about values being used before they are uninitialized, but in this specific case it didn't. So, my question is:
Is this a bug in gcc or is it possible for f(&n) to post-initialize the array size in some weird way?
#include <stdio.h>
void f(int * x) {
*x = 8;
}
int main(void) {
int n;
float a[n]; // Compiler should warn that n may contain garbage
a[7] = 3.1415;
printf("%f\n", a[7]);
f(&n); // Removing this causes the compiler warn as expected
return 0;
}
EDIT: It may be this gcc bug?
GCC is accepting float a[n] as a variable-length array. It should, however, warn you that n contains garbage when it’s used. Perhaps VLA initialization is getting rearranged in a way that makes that fact non-obvious to the code generator? If n were initialized before use, moving the call to f() above the declaration of a would clearly be wrong, but this program produces undefined behavior.
Okay, so this is a stripped down variant of a bug I had. The bug was that I initialized an array using a variable that wasn't initialized. Earlier I used a function to declare the number of elements using a function, but after a cleanup I forgot about it and moved all declarations to the top of the function.
I used the flags -std=c99 -Wall -Wextra -pedantic -O, and usually gcc warns about values being used before they are uninitialized, but in this specific case it didn't. So, my question is:
Is this a bug in gcc or is it possible for f(&n) to post-initialize the array size in some weird way?
#include <stdio.h>
void f(int * x) {
*x = 8;
}
int main(void) {
int n;
float a[n]; // Compiler should warn that n may contain garbage
a[7] = 3.1415;
printf("%f\n", a[7]);
f(&n); // Removing this causes the compiler warn as expected
return 0;
}
EDIT: It may be this gcc bug?
GCC is accepting float a[n] as a variable-length array. It should, however, warn you that n contains garbage when it’s used. Perhaps VLA initialization is getting rearranged in a way that makes that fact non-obvious to the code generator? If n were initialized before use, moving the call to f() above the declaration of a would clearly be wrong, but this program produces undefined behavior.
In slides 137-140 of this presentation, it was mentioned that bar() and maybe even foo() was compiled as inline functions for this sample program, resulting in the printout of a being 42 in normal builds even though it should technically be garbage. Do you happen to know why is the output garbage as expected when the optimizer kicks in?
I've included the source code
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}
int main(void)
{
bar();
foo();
return 0;
}
and the command prompt printout for reference.
$ cc foo.c && ./a.out
42
$ cc -O foo.c && ./a.out
1606415608
Just an educated guess:
In the non-optimized case the compiler reserves a space for the a variable in bar() and initializes it to 42. Then, when foo() is called, it uses the same space for the uninitialized a and prints out 42.
When it's optimized, the initialization of a in bar() is optimized away, because it's not used. Probably, even the call to bar() is eliminated. So, as expected, foo() prints out garbage, that is whatever happens to be in that memory slot (or register) at the time.
a is uninitialised in the first function, which means you can't expect it to have any particular value. So compiler behaviour is correct in both cases (that you get 42 without optimisations is just a coincidence).