2

I know that it's a silly question to ask, but please indulge me for a bit.

What do I know?

  • Scope of a variable x is basically the block within which x was defined. If x belongs to the automatic storage class then, it can only be used within the block where it was declared (which is precisely the scope of x).

  • Lifetime of a variable x is basically the time period during which x is allowed to live in the memory. And if x is an automatic variable then, by default it'll be killed as soon as it's scope is executed.

So, why do I have this question? It is because of the following code I accidentally conjured.

#include<stdio.h> void scratch(void); int main(void) { for(int i = 0; i < 5; i++) scratch(); return 0; } void scratch(void) { static int static_var = 0; int auto_var1, auto_var2 = 0; printf("[info] 'static' variable = %d | 'auto' variable 1 = %d | 'auto' variable 2 = %d\n", static_var++, auto_var1++, auto_var2); } 

Output:

[info] 'static' variable = 0 | 'auto' variable 1 = 0 | 'auto' variable 2 = 0 [info] 'static' variable = 1 | 'auto' variable 1 = 1 | 'auto' variable 2 = 0 [info] 'static' variable = 2 | 'auto' variable 1 = 2 | 'auto' variable 2 = 0 [info] 'static' variable = 3 | 'auto' variable 1 = 3 | 'auto' variable 2 = 0 [info] 'static' variable = 4 | 'auto' variable 1 = 4 | 'auto' variable 2 = 0 

The static variable, static_var does it's job perfectly well. However, notice the behavior of both automatic variables : auto_var1 & auto_var2. According to my understanding of automatic variables, both auto_var1 & auto_var2 should have printed 0 in each iteration, because their lifetime should have ended the moment scratch() returned to main(). But looks like these guys are living a long life. So my question is why? Does this mean that an automatic variable also has a lifetime equal to that of static variable in the same block? OR is it a compiler based issue?

Note : The above code was compiled via gcc version 7.2.0 on Ubuntu 17. Also, a request to everyone who attempts to answer this question : kindly stick to C for answering.

4
  • 7
    Undefined behavior. Commented Dec 23, 2019 at 5:55
  • if the static variable is destroyed after going out of the function then how can its value be retained the next time you call the function? Commented Dec 23, 2019 at 6:40
  • @phuclv, for static variables I believe that, they live for as long as the program exists. However they can only be used within their scope. And because they've a long life, therefore they're able to retain their values beyond their scope. Commented Dec 23, 2019 at 6:55
  • 3
    The value of the uninitialized variable auto_var1 is indeterminate, and using an indeterminate value results in undefined behaviour in C. Commented Dec 23, 2019 at 9:20

4 Answers 4

2

Do automatic variables have lifetime equal to that of a static variable (within the same block)?

Short answer - No, an object with automatic storage duration is limited to the block within which it is declared. An object with static storage duration remains valid for the life of the program. See C11 Standard - 6.2.4 Storage durations of objects

Now within the same block, from the standpoint of either object, both remain valid for the life of the block. The one with automatic storage duration will simply cease to be accessible after leaving the block while the one with static storage duration can be accessed elsewhere.

Sign up to request clarification or add additional context in comments.

13 Comments

Considering the fact that an automatic variable's scope is also it's lifetime, how can one describe the ability of 'auto_var1' to retain it's value outside of it's lifetime? Or should I simply consider it as an undefined behaviour, as mentioned by @jxh?
auto_var1 can never retain is value outside its lifetime In a way that is legal to access it. The memory is released for reuse. Whether it has been overwritten yet or not is irrelevant -- you cannot access a variable outside its lifetime for that reason - no guarantee - invokes Undefined Behavior. "If an object is referred to outside of its lifetime, the behavior is undefined." (6.2.4(p2))
Okay. Now I get it. The reason, why auto_var1 was able to retain it's value even past it's life was only because there was no overwriting performed at it's memory location. So, this brings me to the question, shouldn't auto_var1 have been reallocated with new memory location every time scratch() gets called? Why reallocate it with the same memory location?
YEP!!! Bingo... The value is still there -- but you can't legally access it -- no guarantee it will be there. So we treat every variable that goes out of scope as untouchable...
@Argon - the last piece of the puzzle you are missing is that every time scratch() is called, a separate Function Stack is created, and all local variables are declared within that block of memory. When the function returns, the function stack is released for re-use. So every call to scratch() is a new and separate function stack setup (now does that mean the compiler cannot be smart enough to optimize that somehow to minimize system resources -- of course it can -- but how and whether the compiler optimizes some aspects of the call isn't something the standard allows you to rely on.)
|
1

First variable is not initialized, you are printing garbage there.

int auto_var1, auto_var2 = 0; 

Change this to initialize both, they'll both print 0 then.

int auto_var1 = 0, auto_var2 = 0; 

1 Comment

Agreed. However, what can you say about their lifetime?
1

The other answers tell you what you should do according to the C standard, I'll explain why it happens to work as it does.

In order to keep track of where functions should return and the values of their automatic variables, most platforms define a data structure called the stack, which may be grown or shrunk at one end. When a function f calls another function g, first a return address is pushed onto the stack and then g's automatic variables are created on the stack. When g returns, it deallocates its automatic variables and pops the return address from the stack and jumps to it, returning the stack to its state before the function call.

In your scratch function, auto_var1 is not initialised, so when the variable is created, nothing is written to it; it contains whatever happened to appear in that place in memory. The first time scratch was called, it happened to contain zero, so it was incremented to 1, and then the variable was destroyed. In subsequent calls, however, the stack layout is exactly the same as the first time scratch was called and so auto_var1 was allocated in the same place where it was before. Since the variable is still not initialised, the value is incremented again, and the variable appears to behave as if it was static.

Now, it's only by sheer luck that this works this way: it's because the stack layout is exactly the same in each call. If for example you had a call mainscratch and then mainfunc → scratch, this would no longer be the case. And even the above explanation is still somewhat simplified; for example, the compiler may choose to keep a variable in a CPU register instead of the stack, and reuse the register between function calls, so even keeping the call graph the same does not guarantee it working this way.

Even aside from all that, the code still exhibits undefined behaviour according to the C standard. The compiler is not guaranteed to preserve this behaviour in different circumstances, and it is not portable to different architectures; on some, like Itanium, the code may outright crash. Uninitialised memory is not even guaranteed to be 'stable': reading the same address more than once may yield different values each time.

Moral of the story: initialise your variables before reading them.

1 Comment

Thankyou for adding this perspective to the picture. Helped me to shape my entire thought process.
0

The value of auto_var1 shows undefined behavior where it seems the variable keeps its value between function calls and gets incremented, just as the static variable does.

If you are running on Intel then this can be explained as follows:

The for loop does not change the stack and each time it calls function scratch() it modifies the stack in the same way by pushing return address, saving registers and then incrementing the stack pointer to create room for the local variables int auto_var1 and auto_var2. The second one is initialized to zero, the first one is not initialized, so the value in that memory location (variable) is the same as from the previous function call. The memory location gets incremented and the function returns. The next iteration increments it again and again.

The only point to notice is that in one or another way, the memory location was zero on the first iteration. This is a coincidence.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.