-1

I recently learnt about the static variables, that they retain their values in between various function calls. Then I wrote some code to test it, then hopefully it worked perfect. But then I accidentally removed the static keyword at the beginning of the declaration of the local variable and there came the actual problem. The output of both the programs are similar, besides the absence of the static keyword during the declaration.

Code without any static declaration:

#include <stdio.h>

void up();

int main(){
    up(); //Output: 1
    up(); //Output: 2
    return 0;
}

void up(){
    int stvar;
    stvar++;
    printf("%d\n", stvar);
}

Code with static declaration:

#include <stdio.h>

void up();

int main(){
    up(); //Output: 1
    up(); //Output: 2
    return 0;
}

void up(){
    static int stvar;
    stvar++;
    printf("%d\n", stvar);
}

Then finally I tried this one, by just initializing the local variable:

#include <stdio.h>

void up();

int main(){
    up(); //Output: 1
    up(); //Output: 1
    return 0;
}

void up(){
    int stvar = 0;
    stvar++;
    printf("%d\n", stvar);
}

This time the local variable shows it natural behaviour. I just wanted to know if uninitialized local variables are static by default?

Yun
  • 3,056
  • 6
  • 9
  • 28
Uday Kiran
  • 35
  • 5
  • No, they are not `static`. They are `auto` by default. – Ian Abbott Sep 08 '21 at 10:02
  • You can test it by calling some other function with initialized local variables inbetween the calls to `up`. (But the behavior is undefined, so the results might not be meaningful.) – Ian Abbott Sep 08 '21 at 10:05
  • Read more at [this question and answer](https://stackoverflow.com/questions/51329671/difference-between-static-global-variable-and-non-static-global-variable-in-c/51329933#51329933). – Steve Summit Sep 08 '21 at 11:48

2 Answers2

3

No, they are not static by default. In principle, the initial value can be anything at all. Using the value could even be undefined behaviour. In practice, the compiler picks a memory location for the variable on the stack, and the variable's initial value is whatever happens to already be in that memory location.

Since you don't run any other code between the first up() and the second up(), in practice your program is likely to pick the same location twice and therefore it still has the previous value. If you called another function in between, that function's local variables would go in the same space previously used by up()'s local variables, which would overwrite the value from the first up().

You certainly can't rely on it. Even if you don't call any other functions in-between, the compiler might add one "secretly" (for various reasons). Or the compiler may decide to adjust the stack between the two calls to up so each call might get a different stack location for its local variables.

You also aren't guaranteed that the first value is 0. Because it's whatever happens to be at that memory location already, it could be something left over from a previous function. main isn't the first function that gets called; there is some function in the standard library which does set-up work before it calls main.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • It is misleading to say that “In practice, the compiler picks a memory location for the variable on the stack, and the variable's initial value is whatever happens to already be in that memory location.” With good modern compilers, this happens only in certain conditions, such as optimization is turned off (which does not happen “in practice”; deployed projects almost always use optimization), the address of an object is taken, or a function’s local variables are so large or numerous that registers are insufficient… – Eric Postpischil Sep 08 '21 at 11:12
  • … In the latter case, a stack location may not be used exclusively for one variable; the compiler may just spill temporarily when it runs out of registers and use the same stack location for multiple things at different times. Further, compiler optimizations have been increasing in aggressiveness so that an uninitialized local variable might not be tied to a memory location or register at all; when the compiler recognizes it is uninitialized, it might remove or omit any memory loads or register moves to get its value, resulting in it acting as if it has different values at different times. – Eric Postpischil Sep 08 '21 at 11:14
2

Using non initialized automatic variable is dangerous. It is Undefined Behavior if no address is taken. Otherwise, like in your case the value of stvar would be indeterminate. It's value can be arbitrary, it may even change between accesses.

Always initialize local variables.

Note that variables with static storage (globals and static) are zero initialized if not initialized explicitly.

tstanisl
  • 13,520
  • 2
  • 25
  • 40
  • Use of an uninitialized automatic object is not always undefined behavior. For example, using an uninitialized automatic array never has undefined behavior. The rule that gives it undefined behavior, C 2018 6.3.2.1 2, applies only if the object’s address is not taken (and any access to an array element implicitly takes its address). – Eric Postpischil Sep 08 '21 at 11:17
  • @EricPostpischil, Interesting, does it mean that `int stvar;` is used and put a dummy `&stvar;` then the behavior would not be undefined? what would be the value of `stvar` in such a case? Unspecified? – tstanisl Sep 08 '21 at 11:27
  • Yes, a “useless” `&stvar;` statement would satisfy the criterion of taking the address of the object. Its value would then be *indeterminate*, meaning the C standard does not require it to hold any fixed value. Whenever it is used, e.g., in a `printf` statement, the compiler does not have to load anything from whatever memory might be assigned for the object—it can allow whatever happens to be in a register to be used, even if that is a different register from the last use. – Eric Postpischil Sep 08 '21 at 11:30
  • @EricPostpischil, take a look on https://godbolt.org/z/jvrG67bPj , compiled with "clang -O2" the function jumps to `doe`! It should not be possible if there was no *UB*. Or clang does something wrong – tstanisl Sep 08 '21 at 11:35
  • It is entirely possible for that code to call `doe` without undefined behavior. Since the value of `i` is indeterminate, it may be 1 in `if (i == 0)` and 0 in `if (i != 0)`. – Eric Postpischil Sep 08 '21 at 11:37
  • Now I understand, I've confused UB with usage of indeterminate values. I'll update the answer, thanks – tstanisl Sep 08 '21 at 11:39