简体   繁体   中英

Working of “function calls” on stack?

main() calling f1(), f1() calling f2(), f2 calling f3(), f3() calling f4() and so on...

A function calls another function and chain goes on.

|        |
| f4()   |
| f3()   |
| f2()   |
| f1()   |
| main() |
__________

When a function is called a structure named Activation record is prepared that contains the information associated with it's call. That activation record regarding each function is pushed on to a stack called program-stack or run-time-stack . I have no good idea either about the Activation record or Run-time-stack . My problem is :

  1. A function calls another function and chain goes on. But for how long? how long this nested calls can go on? And What do they depend on? Is it OS or bit architecture of the machine ?

  2. What does Activation Record contain? What is its structure like? Is local data also passed to this?

  3. What parameters are set for stack overflow of such type of functions(which have several nested calls or recursive functions) ? I mean how to know in advance to avoid overflow .

An "activation record" is an abstract concept, useful for academics performing formal analysis of algorithms and not too concerned by the actual implementation.

Real systems have function arguments, local variables, saved register values, return addresses, etc. Some systems use CPU registers for some of all of this information.

As the stack pointer is often one of the registers saved, in many environments one can "walk" the stack by using a frame pointer register to locate the saved value of the stack pointer and frame pointer as it existed in the caller, repeating as necessary. If a compiler setting such as "frame pointer omission" is activated (or the system doesn't use a frame pointer register), this can instead be done using the stack pointer combined with debug metadata.

If you're writing such a stack walking algorithm, or computing total stack usage, you need to be concerned with stack layout. It also is useful in understanding how a buffer overflow in a stack variable can lead to overwriting the return address in a "return to libc" exploit.

But for most purposes, like explaining how recursive functions have multiple instances of local variables, or lifetime of aliases (pointers and references) to local variables, the conceptual model of a stack of activation records is sufficient.

If your function creates local variables on the stack you will see the address of that variable change as the stack gets deeper. You could get a rough idea of how much of the stack you have used up by keeping track of that number as it changes. Do this for your education - not in actual code. Something like this:

#include <stdio.h>

int foo(void);
char *sp;

int main(void){
  char a;
  sp = &a;
  foo();
  return 0;
}

int foo(void) {
  char c;
  long depth;
  depth = sp - &c;
  if(depth < 1000) {
    printf("depth is %ld\n", depth);
    foo();
  }
  return 0;
}

On my machine it seems that the stack grows by 48 bytes for each subsequent call to foo() .

"Activation record" is synonymous with "stack frame" - I had to look this up as I'm not familiar with that term.

  1. In most systems, the stack begins with the maximum memory address and then grows down. A certain amount of memory is allocated for the stack (and a stack is created for each thread). Frames can be added to the stack until it hits the limit and then it becomes a stack-overflow. This generally only happens when a recursive function calls itself too many times - it is incredibly rare for a non-recursive call to cause a stack-overflow (one possibility is a function allocates a large amount of memory on the stack).
  2. The contents of a stack frame are detailed on wikipedia ( http://en.wikipedia.org/wiki/Call_stack ) - generally it contains the function call's return address and space for each stack-allocated (aka "local" or "automatic") variable.
  3. Generally there is no way for a program to inspect its own call-stacks at runtime. The only way to avoid a stack-overflow is to keep track of the depth count yourself like so:

function beginFoo() {

    foo(0);
}

function foo(int depth) {
    if( depth > 10 ) return; // avert possible stack-overflow
    foo(depth + 1);
}

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