Scope of a variable. Internal working basic - c

This is a very basic question about the scope of a variable suppose. I have the fiollowing code:
int main()
{
int *p;
p=func();
printf("%d",*p);
return 0;
}
int *func()
{
int i;
i=5;
return &i;
}
My question
The scope of i is finished in func() but, since I am returning the address of i will I be able to access and print5 in main()?
If not, why? does the compiler puts a garbage value in that address space (I don't think this is done).
What actually it means by the scope of a variable is ended ? Also does the memory allocated to i is freed when its scope ends?

Scope of the variable is the region where it can be accessed.
Lifetime of the variable is the time till when the variable is guaranteed to exist.
In your case lifetime of i is within the function not beyond it. It means i is not guaranteed to exist beyond the function. It is not required to and it is Undefined Behavior to access a local variable beyond the function.
The scope of i is finished in func() but, since I am returning the address of i will I be able to access and print 5 in main()?
You might, but it is Undefined Behavior. So don't do it.
If not, why? does the compiler puts a garbage value in that address space (I don't think this is done)
The compiler may put whatever it chooses to in that location, once the function returns the address location is holds an Indeterminate value.
What actually it means by the scope of a variable is ended ? Also does the memory allocated to i is freed when its scope ends?
i is a automatic/local variable and all automatic variables are freed once the scope {,} in which they are declared ends. Hence the name automatic.

It is undefined behaviour to access a variable after it has gone out of scope. This means it is not possible to say what will definitely happen. In the posted code, 5 might be printed, some other value may be printed or some other behaviour may occur (access violation for example).

Behaviour in your example is undefined. Your printf probably will output 5 but that'd be down to luck rather than good design.
In this case, when the scope of the variable is ended, further function calls may reuse the stack address &i changing the value your p variable points to.

No, accessing a variable that has gone out of scope leads to undefined behavior. The storage where thev variable used to be has been reclaimed, so you're likely to overwrite something else which can lead to crashes or just unpredictable behavior.

Your function will probably print 5, but you should never do this. It's undefined behavior since your program no longer owns the location pointed to by the pointer your return (in other words, your program no longer owns i).
Basically each time a function is called, the stack pointer is pushed down to accommodate the new stack frame. When the function call ends, the stack pointer is raised back up. This means that if a different function were to be called, it would have overlapping the same stack space as the previous function call.
To illustrate this a little better, consider this:
int main()
{
int *p;
p=func();
printf("%d\n",*p);
func2();
printf("%d\n",*p);
return 0;
}
int *func()
{
int i;
i=5;
return &i;
}
void func2()
{
int i = 1;
}
There's a pretty good chance that the output would be 5 1. This is because the second call will reuse the same stack space.
(Note that above code snippet is horrible -- you should never do something like that -- it's undefined behavior and highly implementation dependent.)
To answer your questions directly:
The scope of i is finished in func() but, since I am returning the address of i will I be able to access and print5 in main()?
No. You can, but you shouldn't. Such is the beauty of C. Depending on the compiler/OS/etc it might output 5, or it might output random garage.
If not, why? does the compiler puts a garbage value in that address space (I don't think this is done).
The space used for local variables is reused. The first half of the answer hopefully illustrated how this works. (Well, how it typically works.)
What actually it means by the scope of a variable is ended ? Also does the memory allocated to i is freed when its scope ends?
Stack based memory allocation is what's going on behind the scenes.

Related

Pointer allocation and Memory Location [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 12 months ago.
In an Microprocessor it is said that the local variables are stored in stack. In my case if func1() is called by main function then the local variable (int a = 12;)will be created in stack. Once the Called Function is executed the and return back to main function the stack memory will be deleted. So the pointer address still holds (*b) the value 12. At stack if this 'a = 12' is deleted then 'b' should be a dangling pointer no?? Can anyone explain this ? If you have detailed explanation on what happens in memory when this code is being executed it would be helpful.
#include <stdio.h>
int* func1(void);
int main()
{
int* b = func1();
printf("%d\n",*b);
}
int* func1(void)
{
int a = 12;
int* b = &a;
return b;
}
The pointer is dangling. The memory may still hold the previous value, but dereferencing the pointer invokes undefined behaviour.
GCC will give you a warning about this, if you pass -Wall option.
From the C standard (6.2.4):
The lifetime of an object is the portion of program execution during
which storage is guaranteed to be reserved for it. An object exists,
has a constant address,25) and retains its last-stored value
throughout its lifetime.26) If an object is referred to outside of its
lifetime, the behavior is undefined. The value of a pointer becomes
indeterminate when the object it points to reaches the end of its
lifetime.
There are multiple layers here.
First, is the C programming language. It is a language. You say stuff in it and it has meaning. There are sentences that have meaning, but you can also construct grammatically valid sentences that are gibberish.
The code you posted, grammatically valid, is gibberish. The object a inside func1 stops existing when the function returns. *b tries to access an object that does not exists anymore. It is not defined what should happen when you access an object after its lifetime ended. You can read about undefined behavior.
Memory exists. It's not like it is disintegrated when a function returns. It's not like RAM chips are falling out of your PC. They are still there.
So your compiler will produce some machine instructions. These machine instructions will execute. Depending solely on your compiler decisions (the code is undefined behavior, compiler can do what it wants) the actual code can behavior in any way the compiler decides to. But, most probably, *b will generate machine instructions that will read the memory region where object a resided. That memory region may still hold the value 12 or it may have been overwritten somewhere between func1 returning and calling printf, resulting in reading some other value.
At stack if this 'a = 12' is deleted then 'b' should be a dangling pointer no?
Yes.
what happens in memory when this code
This depends on the actual machine instructions generated by the compiler. Compile your code and inspect the assembly.
Strictly speaking, the behaviour is undefined, but repeat the printf or otherwise inspect (in your debugger for example) *b after the printf. It is likely that it will no longer be 12.
It is not accurate to say that the stack memory is "deleted". Rather the stack pointer is restored to the address it had before the call was invoked. The memory is otherwise untouched but becomes available for use by subsequent calls (or for passing arguments to such calls), so after calling printf it is likely to have been reused and modified.
So yes, the pointer is "dangling" since it points to memory that is no longer "valid" in the sense that it does not belong to the context in which the pointer exists.

Why can I access a pointer declared in a function when I'm trying to access it from outside the function in c language?

Happy new year everyone.
I am studying C language. I had a question when some code run about pointer.
#include <stdio.h>
int * b() {
int a = 8;
int *p = &a;
printf("the addr of a in b: %p\n", p); the addr of a in b: 0x7ffccfcba984
return p;
}
int main () {
int *c = b();
printf("the addr of a in main: %p\n", c); // the addr of a in main: 0x7ffccfcba984
printf("The value of ptr is : %d\n", *c ); // 8
return 0;
}
Can you feel something odd in this code?
I learned that a variables declared inside a function is deallocated at the end of the function.
However, I can still access variables outside the function
like above code when trying to access the address of "a" variable. If the deallocation is true, int a should be deallocated at the end of the b function. It is like a free is not used after variables is declared.
Is there some knowledge I am missing about deallocation?
Could you tell me why I can still access it?
Once you leave a function variables "fall out of scope" meaning they are no longer valid.
Using the address of an out of scope variable breaks that boundary and leads to undefined behaviour, as in, it's not valid to do. The &a pointer is effectively invalidated when you exit that function. If you use it then the program may behave erratically, might crash, or might work fine. It's not defined what happens.
In this trivial example you're not going to get the same behaviour as in a real program. Make another function call to a function that exercises the stack and you'll likely see some problems since the stack is being re-used.
Local variables aren't "allocated" per-se, they are simply scoped, and when that scope is exited they are invalidated.
In something like C++ there may be a deallocation process when things fall out of scope, as that language can define destructors and such, but that's not the same as C. In C they just cease to exist.
I learned that a variables declared inside a function is deallocated at the end of the function.
If the deallocation is true, int a should be deallocated at the end of the b function.
Yes. You are not wrong.
A variable will be destructured when it goes out of its scope. Although you use a pointer variable to save a pointer to that variable, accessing it through that pointer is actually an undefined behavior.
Yes, you can access it and see the results you expect because of luck or environment, but it may still cause a crash, unexpected results, etc. Because this behavior is undefined and wrong.

What does fflush() do in terms of dangling pointers?

I came across this page that illustrates common ways in which dangling pointes are created.
The code below is used to illustrate dangling pointers by returning address of a local variable:
// The pointer pointing to local variable becomes
// dangling when local variable is static.
#include<stdio.h>
int *fun()
{
// x is local variable and goes out of scope
// after an execution of fun() is over.
int x = 5;
return &x;
}
// Driver Code
int main()
{
int *p = fun();
fflush(stdout);
// p points to something which is not valid anymore
printf("%d", *p);
return 0;
}
On running this, this is the compiler warning I get (as expected):
In function 'fun':
12:2: warning: function returns address of local variable [-Wreturn-local-addr]
return &x;
^
And this is the output I get (good so far):
32743
However, when I comment out the fflush(stdout) line, this is the output I get (with the same compiler warning):
5
What is the reason for this behaviour? How exactly is the presence/absence of the fflush command causing this behaviour change?
Returning a pointer to an object on the stack is bad, as you've mentioned. The reason you only see a problem with your fflush() call in place is that the stack is unmodified if it's not there. That is, the 5 is still in place, so the pointer dereference still gives that 5 to you. If you call a function (almost any function, probably) in between fun and printf, it will almost certainly overwrite that stack location, making the later dereference return whatever junk that function happened to leave there.
This is because calling fflush(stdout) writes onto the stack where x was.
Let me explain. The stack in assembly language (which is what all programming languages eventually run as in one way or another) is commonly used to store local variables, return addresses, and function parameters. When a function is called, it pushes these things onto the stack:
the address of where to continue executing code once the function completes.
the parameters to the function, in an order determined by the calling convention used.
the local variables that the function uses.
These things are then popped off of the stack, one by one, simply by changing where the CPU thinks the top of the stack is. This means the data still exists, but it's not guaranteed to continue to exist.
Calling another function after fun() overwrites the previous values above the top of the stack, in this case with the value of stdout, and so the pointer's referenced value changes.
Without calling another function, the data stays there and is still valid when the pointer is dereferenced.

Is it possible to keep a pointer to a local variable beyond that variable's scope?

Suppose that I use this code:
int *pointer;
if(1) {
int num = 5; // local variable, can't be used outside the if block.
pointer = &num
}
Is this a safe way to keep track of the num variable? I know this code will work. But I think the compiler will use the old num memory to allocate new variables, causing pointer to refer to an unpredictable value. Is that true?
No, it's not safe. When the closing } of the if is reached, num's lifetime ends, and the value of pointer becomes indeterminate. Using it thereafter invokes undefined behaviour.
What the compiler actually does depends, it may well use the storage that is used for num for another local variable that is not used before num comes into existence. Then using pointer to obtain the value that num had would definitely fail.
No, it probably won't work, as you a trying to keep the value of an object that goes out of scope and the stack frame it belongs to will be destroyed. This might work once or twice, but it is always undefined behavior.

Ampersand bug and lifetime in c

As we know, local variables have local scope and lifetime. Consider the following code:
int* abc()
{
int m;
return(&m);
}
void main()
{
int* p=abc();
*p=32;
}
This gives me a warning that a function returns the address of a local variable.
I see this as justification:
Local veriable m is deallocated once abc() completes. So we are dereferencing an invalid memory location in the main function.
However, consider the following code:
int* abc()
{
int m;
return(&m);
int p=9;
}
void main()
{
int* p=abc();
*p=32;
}
Here I am getting the same warning. But I guess that m will still retain its lifetime when returning. What is happening? Please explain the error. Is my justification wrong?
First, notice that int p=9; will never be reached, so your two versions are functionally identical. The program will allocate memory for m and return the address of that memory; any code below the return statement is unreacheable.
Second, the local variable m is not actually de-allocated after the function returns. Rather, the program considers the memory free space. That space might be used for another purpose, or it might stay unused and forever hold its old value. Because you have no guarantee about what happens to the memory once the abc() function exits, you should not attempt to access or modify it in any way.
As soon as return keyword is encountered, control passes back to the caller and the called function goes out of scope. Hence, all local variables are popped off the stack. So the last statement in your second example is inconsequential and the warning is justified
Logically, m no longer exists when you return from the function, and any reference to it is invalid once the function exits.
Physically, the picture is a bit more complicated. The memory cells that m occupied are certainly still there, and if you access those cells before anything else has a chance to write to them, they'll contain the value that was written to them in the function, so under the right circumstances it's possible for you to read what was stored in m through p after abc has returned. Do not rely on this behavior being repeatable; it is a coding error.
From the language standard (C99):
6.2.4 Storage durations of objects
...
2 The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address,25) and retains
its last-stored value throughout its lifetime.26) If an object is referred to outside of its
lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when
the object it points to reaches the end of its lifetime.
25) The term ‘‘constant address’’ means that two pointers to the object constructed at possibly different
times will compare equal. The address may be different during two different executions of the same
program.
26) In the case of a volatile object, the last store need not be explicit in the program.
Emphasis mine. Basically, you're doing something that the language definition explicitly calls out as undefined behavior, meaning the compiler is free to handle that situation any way it wants to. It can issue a diagnostic (which your compiler is doing), it can translate the code without issuing a diagnostic, it can halt translation at that point, etc.
The only way you can make m still valid memory (keeping the maximum resemblance with your code) when you exit the function, is to prepend it with the static keyword
int* abc()
{
static int m;
m = 42;
return &m;
}
Anything after a return is a "dead branch" that won't be ever executed.
int m should be locally visible. You should create it as int* m and return it directly.

Resources