简体   繁体   中英

How to get the address (pointer) of a variable from a function without using return

For the following C code how can I get the address (pointer) of a from foo() function to the main() function?

  • For some reasons, I cannot use return in foo()
  • From main() function, I do not know the data type of a
void foo(void *ptr){
    int a = 12345;
    ptr = &a;
    printf("ptr in abc: %d\n",ptr);
}

int main() {

    void *ptr;
    foo(ptr);

    printf("ptr in main: %d\n",ptr);
    //printf("a in main: %d\n",*ptr);       //print the value of a (ie. 12345)


    return 0;
}

How to get [anything] from a function without using return

A way to get things from inside a function to the outside without returning is to use indirection. Pass a pointer to some object as an argument, and indirect through the pointer inside the function to set the value of the pointed object.

From main() function, I do not know the data type of a

You can point to any object using a void pointer without having to know the type of the object.

To put these things together:

int main(void) {
    void* ptr;  // a variable to store the address
    foo(&ptr);  // pass pointer to the variable
                // ptr now points to where a used to be
}

void foo(void** ptr){
    int a = 12345;
    *ptr = &a;  // set the pointed variable
}

Most importantly however : The local object a no longer exists after foo has returned, and therefore the pointer is dangling, and there is not much useful that can be done with it. As such, this is a rather pointless exercise.

There are 2 main problems with your function foo .

The first one, which is why the program does not compile, is the return type of foo . Because it is void you cannot return any values from it.

The other problem which will lead to undefined behavior is that your variable a is running out of scope. If you want to access it after it runs out of scope it has to be allocated on the heap (eg with new).

For some reasons, I cannot use return in foo()

Because you declared foo as having return type void . If you chance that, you can use it:

int* foo() {
    int a = 42;
    return &a;
}

However, the calling code can't use that return value since it points to memory that is no longer valid (a local variable in a past function call). This is true regardless of how the calling code gets the pointer: whether it is by returning it, or by passing it to an out parameter. You simply mustn't do this.

From main() function, I do not know the data type of a

Right, because you explicitly declared the pointer as void* and thus erased the data type. Declare the correct data type to avoid this.

Long story short, there's no reason to use a void* parameter instead of an int return value here:

int foo() {
    int a = 42;
    return a;
}

int main(void) {
    int a = foo();
    printf("a in main: %d\n", x);
}

In order to understand WHY you shouldn't try to return a pointer to a local variable, you need to visualize how local variables are allocated in the first place.

Local variables are allocated in the STACK. The stack is a reserved memory area having as main purpose, leaving a "breadcrumb" trail of memory addresses where the CPU should jump once it finishes executing a subroutine.

Before a subroutine is entered (usually via a CALL machine language Instruction in x86 architectures), the CPU will push on the stack the address of the Instruction immediately following the CALL.

ret_address_N
. . . . . . .
ret_address_3
ret_address_2
ret_address_1

When the subroutine ends, a RET urn Instruction makes the CPU pop the most recent address from the stack and redirects execution by jumping to it, effectively resuming execution on the subroutine or function that initiated the call.

This stack arrangement is very powerful, as it allows you to nest a high number of independent subroutine calls (allowing generic, reusable libraries to be built), it also allows recursive calls, where a function can call itself (either directly, or indirectly, by a nested subroutine).

Additionally, nothing prevents you from pushing custom data on the stack (there are special CPU instructions for this) AS LONG AS THE STACK STATE IS RESTORED BEFORE RETURNING FROM A SUBROUTINE , otherwise when the RET Instructions pops the expected return address, it will fetch garbage and it will try to jump execution to it, most likely crashing. (Incidentally, this is also how many malware exploits work, by overwriting the stack with a valid address, and forcing the CPU to jump to malicious code when it performs a RET instruction)

This stack feature may be used, for example, to store the original state of the CPU registers that are modified inside a subroutine - allowing the code to restore their values before the subroutine exits so that the caller subroutine can see the registers in the same state as they were BEFORE performing the subroutine CALL.

Languages like C also use this feature to allocate local variables by setting up a Stack Frame . The compiler basically adds up how many bytes are required to account for every local variable in a certain subroutine, and will emit CPU instructions that will displace the top of the stack by this computed byte amount when a subroutine is called. Now every local variable can be accessed as a relative offset to the current stack's state.

-------------
-------------   local variables for subroutine N
-------------
ret_address_N
-------------   local variables for subroutine 3
ret_address_3
-------------   local variables for subroutine 2
-------------
ret_address_2
-------------
-------------   local variables for subroutine 1
-------------  
-------------
ret_address_1

Besides emitting instructions to set up the stack frame (effectively allocating local variables on the stack and preserving current register values), the C compiler Will emit instructions that will restore the stack state to its original state before the function call, so the RET Instruction can find at the top of the stack the correct memory address when it pops the value it should jump to.

Now you can understand why you can not should not return a pointer to a local variable. By doing so, you are returning an address to a value that was stored temprorarily in the stack. You can dereference the pointer and MIGHT see what looks like valid data as you immediately return from the subroutine returning the pointer to the local variable, but this data will certainly be overwritten, probably in the very near future, as program execution continues calling subroutines.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM