0

In GCC C, is there a way to push/pop data to the C return stack?

I'm not talking about implementing my own stack (I know how to do that); I mean using the existing C return stack to explicitly push/pop parameters (within the same level of braces, of course).

For example, something like:

extern int bar;

void foo(void) {
  PUSH(bar);

  bar = 12;
  doSomething(); // that depends on the value of bar

  bar = POP();   // restore original value of bar
}

If there were any easy way to do this, I think it would be a cleaner alternative to using a local variable like "oldBar" explicitly.

nerdfever.com
  • 1,652
  • 1
  • 20
  • 41
  • You could do it with inline assembly if that's what you are asking about? – arduic Nov 13 '15 at 20:22
  • Is there some particular use-case you have in mind? – luiscubal Nov 13 '15 at 20:23
  • 5
    How would this be cleaner than using a regular variable? What if you push/pop things out of order? – Kevin Nov 13 '15 at 20:23
  • Possible duplicate: http://stackoverflow.com/questions/17921591/how-do-i-write-the-following-inline-assembly-code-in-c-using-gcc – tonysdg Nov 13 '15 at 20:23
  • What's the use case? Why would you want to do this? Maybe the solution to what you want is different as what you ask for. – Eduard Wirch Nov 13 '15 at 20:23
  • 3
    No, in no way would it be a clean(er) solution. Don't even try it. – JimmyB Nov 13 '15 at 20:29
  • @Kevin It's cleaner because it avoids the syntactic load of a temporary variable. Of course if you push/pop out of order you'll get the wrong thing -don't do that. I come from a Forth background where storing temps on a stack is very natural - there are clarity advantages to avoiding names for things that don't need names. If you're used to assembly language as well, you routinely use PUSH/POP to store temps. This is the same idea. – nerdfever.com Nov 14 '15 at 17:29
  • @nerdfever.com "don't do that" is easier said than done. And what advantages are there to avoiding names? – Kevin Nov 14 '15 at 17:36
  • @Kevin The conventional way (int oldBar = bar ... bar = oldBar) is, in my opinion, ugly. Of course this is a matter of taste and opinion - you may not find it ugly. But any time you have to invent a variable purely to get something "out of the way" temporarily, that - to my mind - is begging for a stack implementation. – nerdfever.com Nov 14 '15 at 17:45
  • 1
    In nearly all C implementations the conventional way is already implemented with a stack except it is way less error prone than doing it yourself. If you want to do something like this then don't use C. – Kevin Nov 14 '15 at 17:51

3 Answers3

8

if you use a temporary variable, it's basically the same thing. The temporary variable is allocated on the stack or optimized to a register.

e.g.

extern int bar;

void foo(void) {
  int tmp = bar
  bar = 12;
  doSomething(); // that depends on the value of bar
  bar = tmp;   // restore original value of bar
}

Apparently C doesn't actually require a stack structure to be used for calls, so this functionality wouldn't make sense. This is claimed in the memory layout section of this article https://www.seebs.net/c/c_tcn4e.html

Quite simply, not every compiler even has a "stack". Some systems don't really have any such feature. Every compiler for C has some kind of mechanism for handling function calls, but that doesn't mean it's a stack. More significantly, it is quite common for function parameters or local variables not to be stored on any "stack", but to be stored in CPU registers. That distinction can matter a lot, and should have been covered, rather than hand-waved away.

Technically, you could also use alloca() (located in alloca.h) to do this, but the only way to deallocate that memory is for the function call to return. It also doesn't really do what you're suggesting. alloca isn't part of the C standard either

Bobby Sacamano
  • 540
  • 4
  • 15
  • The fact that the C runtime doesn't require a stack blew my mind when I first realized it. Upon further reflection, though, I realized, "huh - I guess there are a bunch of ways around it" +1 for the explanation. – tonysdg Nov 13 '15 at 20:34
  • 1
    alloca is not supported by C standard – V. Kravchenko Nov 13 '15 at 20:44
  • I suspect it'd be rather difficult to implement recursion entirely without a stack. On the other hand, you could easily have *multiple* stacks, rather than just one. – EOF Nov 13 '15 at 20:46
  • @V.Kravchenko I suspected that was the case, thanks. – Bobby Sacamano Nov 13 '15 at 20:50
  • Thanks; that is the obvious "normal" way to do it. I'm hoping to avoid the syntactic burden of naming the temp variable. alloca() is almost what I want - it does the push. How to do the pop? – nerdfever.com Nov 14 '15 at 17:34
  • @nerdfever.com The only way to deallocate the memory from alloca is for the function to return. You still need to declare a pointer, to point to the new memory allocated, anyway. Alloca was just a stopgap solution for C89's lack of variable length arrays, as far as I'm aware. – Bobby Sacamano Nov 14 '15 at 20:46
0

If you are writing in pure C, you can use variadic argument function. It is, of course, not a full solution, as stack is cleared after a function call and argument is pushed only when you call a function. But if you need to use it as you described, it might work:

extern int bar;
void doSomething(...);
void foo(void) {
  doSomething(&bar);
  //you do not need here to pop to restore the bar value, as you push a copy of value
}

If you want to do some other actions after push (which I can't even imagine), you can use a wrapper function.

extern int bar;
void doSomething();
void doSomethingWrapper(...) {
   //here arguments that are passed in ... are pushed
   doSomething();
   //and here they are poped
}
void foo(void) {
  //....
  doSomethingWrapper(&bar);
  //....
}

If you just want to manipulate stack in a cunning way the only solution is to use inline assembly. Or you can call function void push(...) to push whatever you want and use longjmp() in body of push() to avoid popping arguments when control flow leaves the function.

V. Kravchenko
  • 1,859
  • 10
  • 12
0

OK, this does what I wanted:

#define PUSHINT(var) int old__##var = var
#define POPINT(var) var = old__##var

extern int bar;

void foo(void) {
  PUSHINT(bar);

  bar = 12;
  doSomething(); // that depends on the value of bar

  POPINT(bar);   // restore original value of bar
}

It would be better if I could guarantee that the "old__" prefix was unique each time, but I don't know how to do that (it's unlikely to be a problem tho).

Also it doesn't truly do a PUSH/POP (altho it does store on the return stack, at least on my hardware). You can't push one value and pop a different one.

But I didn't want to do that in the first place.

I may rename the macros to SAVE() and RESTORE()...

(Thanks to the hint from @BobbySacamano's answer.)

nerdfever.com
  • 1,652
  • 1
  • 20
  • 41
  • This still doesn't really have the functionality of a stack, since you can't push the same variable twice, in the same context. I don't see why you have a problem with just naming the variable something like `tmp` or `tmp2` if you have multiple temporary variables. – Bobby Sacamano Nov 14 '15 at 20:49
  • Actually, you could wrap the temporary variable and the call in a compound statement, to give it temporary scope, if namespace pollution bothers you. – Bobby Sacamano Nov 14 '15 at 21:08
  • @BobbySacamano What bothers me is the *mental* load when reading the code with an unnecessary named variable. My solution fixes that, but you're right it doesn't have the full functionality of a stack. I'd still like to know how to do that (but to solve a different problem, not the one I posted above...) – nerdfever.com Nov 14 '15 at 22:20
  • no matter what solution you come to, it's going to be a lot more mental load than simply having some temporary variables. – Bobby Sacamano Nov 14 '15 at 22:22