0

I want to make a function that returns a pointer to a locally-defined variable. For the sake of argument, I'll call the function struct awesome *makeCoolStruct() (I don't want to declare the variable in the outer scope which should have the call struct awesome coolness = makeCoolStruct(); because the function will be called in more than one place).

I understand why the compiler errors out when I try doing that - the local variable will be 'destroyed' as soon as the program exits that scope, so the pointer will basically be invalid.

I looked up how to return a pointer to a local variable in a function, and while I get why malloc would work, that defeats the purpose of trying to do it this way, because malloc, as far as I understand, keeps the variable for as long as the program runs - or until free is called. However, that means that I'll have to have free called at the end of anything that calls my function. At that point, I'd be better off declaring the variable in every calling scope and then passing a pointer to it like makeCoolStruct(&existingVariable);.

If, however, there's a way to declare "keep this variable for 1 position higher in the stack" (which would obviously produce a compilation error in the global scope since there are no higher positions in the stack), then I could return a pointer and it'll exist just fine for as long as I need it to, wherever I decide to call struct awesome x = makeCoolStruct(); (I'm aware of the mutability issues - I'm not looking for mutability). I could truly return anything I want in a function from that point on, not just stuff that was passed in.

I haven't really tried anything because I don't know of anything that would work.

I expect to be able to return a pointer to a local variable after doing whatever keeps the variable for one scope higher, without having to do any preparations for the function call when I call it (creating a variable for it to use for the output instead of just assigning the output to something.

Liran
  • 1
  • 2
  • `struct awesome coolness = makeCoolStruct();` is implying signature `struct awesome makeCoolStruct(void)`. Note that it is not a pointer. – Eugene Sh. Oct 11 '19 at 21:05
  • 1
    Could you just create the variable in the calling function and pass the address of that into the called function? You wouldn't be returning it, but it would still be modified. – Reticulated Spline Oct 11 '19 at 21:05
  • You could return the structure instead of returning a pointer. – Barmar Oct 11 '19 at 21:06
  • @ReticulatedSpline He actually mentions that in the third paragraph. He's just wondering if there's an alternative. – Barmar Oct 11 '19 at 21:07
  • 2
    Passing the pointer is the usual way this is done. There's no way to do what you want with scope. – Barmar Oct 11 '19 at 21:08
  • @ReticulatedSpline Of course I could, but that's exactly what I'm trying to avoid here. I specifically **don't** want to have to remember to prepare a variable for my function before calling it. – Liran Oct 11 '19 at 21:09
  • 1
    Actually, what you're talking about isn't really scope, it's lifetime. Scope refers to where the name of a variable can be used, lifetime refers to when the value can be used. They're related, but not the same thing. – Barmar Oct 11 '19 at 21:09
  • 1
    @Liran AFAIK you either need to manage the memory manually and use `free()`, declare a variable before, or just return the struct directly. Do all of those methods not work? – Reticulated Spline Oct 11 '19 at 21:13
  • 1
    @Barmar Doesn't that mean that upon assignment, the program will copy the structure over? I'm working with an 8Mhz microcontroller, so I need all of the performance optimisation I can get - and I bet copying an array of 80 2-byte structs in a loop which runs over 100 times/second will cause a huge performance hit. I'm trying to avoid keeping 6 arrays of 80 2-byte structs in a higher scope to save on RAM (we're talking about nearly 1kb here). – Liran Oct 11 '19 at 21:15
  • 1
    Yes, it will copy the structure. I wasn't recommending it, just suggesting it as an alternative. Passing the address is the way this is normally done, to avoid copying. – Barmar Oct 11 '19 at 21:16
  • 1
    The simple answer to your question is that it's not possible to do what you want. You know the alternative, do it. – Barmar Oct 11 '19 at 21:19
  • This is clearly an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). The solution that you propose doesn't exist. So now you need to tell us the actual problem that you're trying to solve. – user3386109 Oct 11 '19 at 21:21
  • As I feared. I'm curious, do you have any idea why increasing the lifetime of variables one position higher up the stack wasn't implemented in C? It seems like an awfully nice idea, you could return things that were created up and up through the stack, just like you can pass them in down and down through the stack. It would make C variables super mobile. Create your variable wherever you want, and get it to any scope you want, simply by using returns and passes. – Liran Oct 11 '19 at 21:22
  • 1
    It's quite simple: the stack pointer is a single register. When you call a function, the stack pointer is decremented to make room for the return address, the old frame pointer, and the local variables. When you return from a function the stack pointer is incremented to the point where it was before the function was called. The next function call (to the same or different function) then reuses the same memory. – user3386109 Oct 11 '19 at 21:29
  • I still don't understand why local variables can be passed into an inner stack, whereas they cannot be returned into an outer stack. I still don't get why do the two behave differently if one is just subtraction and one is addition. – Liran Oct 11 '19 at 21:43
  • 1
    There's [this SO question](https://stackoverflow.com/questions/1395591/what-is-exactly-the-base-pointer-and-stack-pointer-to-what-do-they-point) and [this wikipedia article](https://en.wikipedia.org/wiki/Call_stack). Beyond that you would need to find a good book on microprocessor architecture, and learn a little assembly code. That way, you could look at, and understand, the assembly generated by the compiler for each of the options shown in RS's answer. – user3386109 Oct 11 '19 at 21:58

2 Answers2

3

What you're asking isn't possible in C, but here are some alternatives:

Passing a stack variable in:

typedef struct {
    int a;
    int b;
} Foo;

void bar(Foo* foo)
{
    foo->a = 5;
}

int main(void)
{
    Foo foo = {0};
    bar(&foo);
    return 0;
}

Returning the struct directly:

Foo bar(void)
{
    Foo foo = {1, 2};
    return foo;
}

int main(void)
{
    Foo foo = bar();
    return 0;
}

Managing the memory manually:

Foo* bar(void)
{
    Foo* foo = malloc(sizeof(*foo));
    return foo;
}

int main(void)
{
    Foo* foo = bar();
    free(foo);
    return 0;
}

Lastly, if you really hate whoever is going to inherit this project, you could always use a macro:

#define FOO(X)     \
    Foo X = {0};   \
    bar(&X);
  • The question mentioned the first method, he's just looking for an alternative. – Barmar Oct 11 '19 at 21:18
  • The two options I already had in mind but didn't want to use... Sigh... – Liran Oct 11 '19 at 21:19
  • Right, macros hahaha, if I'm feeling evil hahahaha! I guess they could work... Your answer is now super comprehensive! That's awesome, so many different ways to go about it (especially the macros one which I hadn't thought of before) – Liran Oct 11 '19 at 21:29
0

Although not recommended, it is actually safe to return a pointer to a static variable, assuming your program is not multithreaded.

struct myStruct {
    int x;
    char y;
};

struct myStruct *foo() 
{
    static struct myStruct bar = {.x = 0, .y = 'a'};
    return &bar;
}

I would never write something like that, but it is safe in the sense that it does not invoke undefined behavior.

And just in case you did not know, it's perfectly cool to return a struct. You don't need malloc for that. This would be fine:

struct myStruct foo() 
{
    struct myStruct bar = {.x = 0, .y = 'a'};
    return bar;
}
klutt
  • 30,332
  • 17
  • 55
  • 95
  • It is not safe to use a `static` local variable in a multi-threaded program, or (at least) it limits the usefulness of the function in a multithreaded program. It also means you can't have two values being saved up at once. Passing a pointer to the structure into the function or returning the structure value (not a pointer) are usually the best choices. – Jonathan Leffler Oct 11 '19 at 21:14
  • My program isn't multithreaded, but either way using statics like this feels very, very dirty to me. I hate the idea. – Liran Oct 11 '19 at 21:18
  • @JonathanLeffler True. I forgot about multithreading. Answer updated. – klutt Oct 11 '19 at 21:26
  • 2
    This has the same issues with ISRs as it does with multi-threaded systems. While this is an approach with pitfalls and is best avoided, it is a legal one that needed to be mentioned and was missing from the other answer. – Avi Berger Oct 11 '19 at 21:44