ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Distribution: Linux(Redhat,fedora,suse,ubantu), Solaris (s8/s9/s10/nevada/open-solaris)
Posts: 303
Rep:
returning address of stack variable in function
In function fun, address of stack variable(a) is copied to ptr.
But as soon as program control comes out of function, stack variables gets cleaned.
So inside main(), program should not have printed value of a.
But here, I am able to print the content inside main().
Is this expected ?
Code:
#include<stdio.h>
int* fun(int *ptr)
{
int a=40;
ptr=&a;
return ptr;
}
int main()
{
int i=20;
int *ptr=&i;
printf("\n %d",*fun(ptr));
return 0;
}
#include<stdio.h>
int* fun(int *ptr)
{
printf("in fun ptr = %d before reassignment \n", *ptr);
int a=40;
// you are not assigning the prams ptr
//your assigning 'a' to ptr over riding your pram ptr
//so it is returning 40 like you programmed it to do.
ptr=&a;
printf("in fun a = %d ptr = %d\n",a, *ptr);
return ptr;
}
int* fun2(int *ptr) { return ptr; }
int main()
{
int i=20;
int *ptr=&i;
printf("main ptr = %d\n", *ptr);
//your function is printing what is declared
// in your function
printf("in main\n %d\n\n",*fun(ptr));
int *f, s;
s = 32;
f = &s;
printf("fun2= %d\n", *fun2(f));
return 0;
}
results
Code:
$ ./a.out
main ptr = 20
in fun ptr = 20 before reassignment
in fun a = 40 ptr = 40
in main
40
fun2= 32
@OP: What you see is accidental working, but what you should achieve is reliable working.
The memory on the stack might not have been overwritten that moment, but sooner or later it will be overwritten, and your data will be lost. Also stack is allowed to grow and shrink on demand, so a pointer that points to a currently unused stack-area might become invalid pointer that causes segmentation fault when you try to use the pointed value.
In function fun, address of stack variable(a) is copied to ptr.
But as soon as program control comes out of function, stack variables gets cleaned.
So inside main(), program should not have printed value of a.
But here, I am able to print the content inside main().
Is this expected ?
Just because the stack gets popped, does not mean that the locations used by it are corrupt, in fact they typically are not until you make another function call. This does not mean that you're doing a correct thing. You shouldn't be returning a stack variable address to the calling function.
Your code is the reverse of how I'd pass a pointer and use it, crude example. I'd likely check for a NULL pointer and return a status from fun(), neither of which is done here:
This is playing the lottery.
Might work right now, might stop working if you use a different (version of the ) compiler, different optimizations, etc.
It's like using uninitialized variables.
Some hypothetical int in a different code might be initialized 'by itself' to 0x00000000 to you, might do it to 0xdeadbeef for someone else, or yourself at another compilation.
Conclusion:
==============
1. such results are compiler specific.
2. so even after stack unwinding, variables take some time to cleanup.
They don't really cleanup. When a function returns the portion of the stack that it has used is available for other uses. That means the results from continuing to access it in your program are undefined. Amongst the possible outcomes of using a pointer to a function local variable after the function has terminated is that you get the value assigned.
Memory assigned with malloc is allocated on the heap and until freed will remain available to your program so a pointer can be assigned to and returned from a function and safely used. A pointer to a local variable can be passed to a function and assigned then read after the function returns because it is on the calling functions stack, not the receiving function.
Let this be a lesson about the benefits of modern C/C++ tooling.
Code:
❯ cat stack3.c
#include <stdio.h>
int *fun()
{
int a;
int *b;
b = &a;
return b;
}
int main()
{
printf("%d\n", *fun());
return 0;
}
❯ cat Makefile
stack3.o:
gcc -o stack3.o stack3.c
❯ bear make
gcc -o stack3.o stack3.c
❯ clang-tidy stack3.c
2 warnings generated.
stack3.c:8:5: warning: Address of stack memory associated with local variable 'a' returned to caller [clang-analyzer-core.StackAddrEscapeBase]
return b;
^
stack3.c:13:21: note: Calling 'fun'
printf("%d\n", *fun());
^
stack3.c:8:5: note: Address of stack memory associated with local variable 'a' returned to caller
return b;
^
stack3.c:13:5: warning: 2nd function call argument is an uninitialized value [clang-analyzer-core.CallAndMessage]
printf("%d\n", *fun());
^
stack3.c:13:21: note: Calling 'fun'
printf("%d\n", *fun());
^
stack3.c:5:5: note: 'a' declared without an initial value
int a;
^
stack3.c:13:21: note: Returning from 'fun'
printf("%d\n", *fun());
^
stack3.c:13:5: note: 2nd function call argument is an uninitialized value
printf("%d\n", *fun());
^
I wrote a Makefile as an intermediate step towards generating the compilation database (compile_commands.json) needed to get clang's tooling to work, then I ran bear on that Makefile to generate said database. And then I ran the code through clang-tidy. That caught it.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.