Unexpected output regarding local variable - c

I have created a local variable b in the function foo(). This function returns the address of the variable b. As b is a local variable to the functionfoo(), the address of the variable is not supposed to be there as the execution of the function foo() finishes. I get the following warning message:
C:\Users\User\Documents\C codes\fun.c||In function 'foo':
C:\Users\User\Documents\C codes\fun.c|6|warning: function returns address of local variable [-Wreturn-local-addr]|
But the pointer ptr of the main() function successfully receives the address of b and prints it. As far as I know about stack, the address of the local variable b should cease to exist after the control of the program returns to the main() function. But the output proves otherwise.
#include<stdio.h>
int* foo()
{
int b = 8;
return &b;
}
int main()
{
int *ptr;
ptr = foo();
printf("b = %d", *ptr);
return 0;
}
Output:
b = 8

The lifetime of an object is the portion of program execution during which storage (memory) for it is reserved.
When we say an object ceases to exist in the C model, we mean that reservation ends. The memory is no longer reserved for the object. This is merely a bookkeeping operation; the memory might or might not be used for another purpose. The fact that the reservation ends does not mean anything enforces a rule that you must not use the memory.
What happens when you use an object after its memory reservation ends is similar to what happens when you trespass in the real world. Maybe nobody notices and you get away with it. Maybe somebody notices and stops you. Maybe somebody changes what was there, and maybe they do not.
In the case you tested, nothing changed the memory where b was, and you got away with using memory that was no longer reserved for b. But you cannot rely on this; there is no rule that says it will always work.

Related

Function parameters behaving strangely in C

Below is the sample code written in C:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
int* second;
void myTest1(int a, bool check){
if(check){
second = &a;
}
printf("%d", *(second));
printf(" ");
}
int main(int argc, char const *argv[])
{
int a =1;
int b = 2;
int c=3;
myTest1(a,true);
myTest1(b,false);
myTest1(c,false);
}
I expect the output be like
1 1 1
But the actual output is
1 2 3
I am a bit confused about it, void myTest1(int a, bool check) here I believed a should have function scope. But it seems that the memory location of a is reused in every function call.
I am building by using command gcc <filename>.c
Below are some system details:
OS: Ubuntu
GCC compiler version: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
You are setting second to the address of a variable that goes out of scope at the end of the function invocation. Once the variable is out of scope, the memory it occupied is no longer yours, and it can be reused. That memory address happens to be reused in each subsequent invocation, with the new argument's value copied into it.
Don't store the address of a local variable and access that address after the variable has gone out of scope. This produces undefined behaviour. You cannot make any assumptions about what may be on the other end of that pointer.
Regarding the following:
here I believed a should have function scope
It does...
But it seems that the memory location of "a" is reused in every function call.
Well, reuse is a necessary and natural result of a having "function scope". Unless you are assuming some kind of garbage-collection behavior, where keeping a pointer to this memory prevents it from being reused, a's memory location should be reused once a is inaccessible, otherwise this is the very definition of a memory leak. If the memory for function arguments weren't reused, every function invocation would leak memory by design.
In C and C++ it's your job not to store the address of stack-allocated variables that have gone out of scope (or at least, not to try to use that address after the variable has gone out of scope). The act of storing an address in a pointer does not innately protect that memory from reuse. It's up to you to either allocate that memory on the heap and manage its lifetime yourself, or to allow the stack to manage your memory and not hold onto pointers that outlive the lifetime the variables they point to.
You're causing undefined behavior. When a function returns, all its automatic variables are destroyed, and any pointers to them become invalid.
On the second and third calls to myTest1(), second points to a variable from the first call. Since this variable no longer exists, dereferencing the pointer results in undefined behavior.
You're getting the result you see because in practice each successive call to the function happens to use the same location for the stack frame. So the address of a in each call is the same, so the old pointer will point to the value that was passed in the new call.

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.

Scope and lifetime of local variables in C

I would like to understand the difference between the following two C programs.
First program:
void main()
{
int *a;
{
int b = 10;
a=&b;
}
printf("%d\n", *a);
}
Second program:
void main()
{
int *a;
a = foo();
printf("%d\n", *a);
}
int* foo()
{
int b = 10;
return &b;
}
In both cases, the address of a local variable (b) is returned to and assigned to a. I know that the memory a is pointing should not be accessed when b goes out of scope. However, when compiling the above two programs, I receive the following warning for the second program only:
warning C4172: returning address of local variable or temporary
Why do I not get a similar warning for the first program?
As you already know that b goes out of scope in each instance, and accessing that memory is illegal, I am only dumping my thoughts on why only one case throws the warning and other doesn't.
In the second case, you're returning the address of a variable stored on Stack memory. Thus, the compiler detects the issue and warns you about it.
The first case, however skips the compiler checking because the compiler sees that a valid initialized address is assigned to a. The compilers depends in many cases on the intellect of the coder.
Similar examples for depicting your first case could be,
char temp[3] ;
strcpy( temp, "abc" ) ;
The compiler sees that the temp have a memory space but it depends on the coder intellect on how many chars, they are going to copy in that memory region.
your foo() function has undefined behavior since it returns a pointer to a part of stack memory that is not used anymore and that will be overwritten soon on next function call or something
it is called "b is gone out of scope".
Sure the memory still exists and probably have not changed so far but this is not guaranteed.
The same applies to your first code since also the scope of b ends with the closing bracket of the block there b is declared.
Edit:
you did not get the warning in first code because you did not return anything. The warning explicitly refers to return. And since the compiler may allocate the stack space of the complete function at once and including all sub-blocks it may guarantee that the value will not be overwritten. but nevertheless it is undefined behavior.
may be you get additional warnings if you use a higher warning level.
In the first code snippet even though you explicitly add brackets the stack space you are using is in the same region; there are no jumps or returns in the code so the code still uses consecutive memory addresses from the stack. Several things happen:
The compiler will not push additional variables on the stack even if you take out the code block.
You are only restricting the visibility of variable b to that code-block; which is more or less the same as if you would declare it at the beginning and only use it once in the exact same place, but without the { ... }
The value for b is most likely saved in a register which so there would be no problem to print it later - but this is speculative.
For the second code snippet, the function call means a jump and a return which means:
pushing the current stack pointer and the context on the stack
push the relevant values for the function call on the stack
jump to the function code
execute the function code
restore the stack pointer to it's value before the function call
Because the stack pointer has been restored, anything that is on the stack is not lost (yet) but any operations on the stack will be likely to override those values.
I think it is easy to see why you get the warning in only one case and what the expected behavior can be...
Maybe it is related with the implementation of a compiler. In the second program,the compiler can identify that return call is a warning because the program return a variable out of scope. I think it is easy to identify using information about ebp register. But in the first program our compiler needs to do more work for achieving it.
Your both programs invoke undefined behaviour. Statements grouped together within curly braces is called a block or a compound statement. Any variable defined in a block has scope in that block only. Once you go out of the block scope, that variable ceases to exist and it is illegal to access it.
int main(void) {
int *a;
{ // block scope starts
int b = 10; // b exists in this block only
a = &b;
} // block scope ends
// *a dereferences memory which is no longer in scope
// this invokes undefined behaviour
printf("%d\n", *a);
}
Likewise, the automatic variables defined in a function have function scope. Once the function returns, the variables which are allocated on the stack are no longer accessible. That explains the warning you get for your second program. If you want to return a variable from a function, then you should allocate it dynamically.
int main(void) {
int *a;
a = foo();
printf("%d\n", *a);
}
int *foo(void) {
int b = 10; // local variable
// returning the address of b which no longer exists
// after the function foo returns
return &b;
}
Also, the signature of main should be one of the following -
int main(void);
int main(int argc, char *argv[]);
In your first program-
The variable b is a block level variable and the visibility is inside that block
only.
But the lifetime of b is lifetime of the function so it lives upto the exit of main function.
Since the b is still allocated space, *a prints the value stored in b ,since a points b.

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.

Memory Allocation: Why this C program works? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Returning the address of local or temporary variable
The add function is implemented wrongly. It should return a value instead of a pointer.
Why aren't any errors when ans and *ans_ptr are printed and the program even gives correct result? I guess the variable of z is already out of scope and there should be segmentation fault.
#include <stdio.h>
int * add(int x, int y) {
int z = x + y;
int *ans_ptr = &z;
return ans_ptr;
}
int main() {
int ans = *(add(1, 2));
int *ans_ptr = add(1, 2);
printf("%d\n", *ans_ptr);
printf("%d\n", ans);
return 0;
}
The reason it 'works' is because you got lucky. Returning a pointer to a local variable is Undefined Behaviour!! You should NOT do it.
int * add(int x, int y) {
int z = x + y; //z is a local variable in this stack frame
int *ans_ptr = &z; // ans_ptr points to z
return ans_ptr;
}
// at return of function, z is destroyed, so what does ans_ptr point to? No one knows. UB results
Because C has no garbage collection, when the "z" variable goes out of scope, nothing happens to the actual memory. It is simply freed for another variable to overwrite if the compiler pleases.
Since no memory is allocated between calling "add" and printing, the value is still sitting in memory, and you can access it because you have its address. You "got lucky."
However, as Tony points out, you should NEVER do this. It will work some of the time, but as soon as your program gets more complex, you will start ending up with spurious values.
No. Your question displays a fundamental lack of understanding of how the C memory model works.
The value z is allocated at an address on the stack, in the frame which is created when control enters add(). ans_ptr is then set to this memory address and returned.
The space on the stack will be overwritten by the next function that is called, but remember that C never performs memory clean up unless explicitly told to (eg via a function like calloc()).
This means that the value in the memory location &z (from the just-vacated stack frame) is still intact in the immediately following statement, ie. the printf() statement in main().
You should never ever rely on this behaviour - as soon as you add additional code into the above it will likely break.
The answer is: this program works because you are fortunate, but it will take no time to betray, as the address you return is not reserved to you anymore and any one can use it again. Its like renting the room, making a duplicate key, releasing the room, and after you have released the room at some later time you try to enter it with a duplicate key. In this case if the room is empty and not rented to someone else then you are fortunate, otherwise it can land you in police custody (something bad), and if the lock of the room was changed you get a segfault, so you can't just trust on the duplicate key which you made without acquisition of the room.
The z is a local variable allocated in stack and its scope is as long as the particular call to the function block. You return the address of such a local variable. Once you return from the function, all the addresses local to the block (allocated in the function call stack frame) might be used for another call and be overwritten, therefore you might or might not get what you expect. Which is undefined behavior, and thus such operation is incorrect.
If you are getting correct output, then you are fortunate that the old value held by that memory location is not overwritten, but your program has access to the page in which the address lies, therefore you do not get a segmentation fault error.
A quick test shows, as the OP points out, that neither GCC 4.3 nor MSVC 10 provide any warnings. But the Clang Static Analyzer does:
ccc-analyzer -c foo.c
...
ANALYZE: foo.c add
foo.c:6:5: warning: Address of stack memory associated with local
variable 'z' returned to caller
return ans_ptr;
^ ~~~~~~~

Resources