Thread: How would these variables get pushed on to the stack?

  1. #1
    Registered User
    Join Date
    May 2020
    Posts
    13

    How would these variables get pushed on to the stack?

    As far as I know, the stack is a region in RAM that stores local variables created by each function (including the main() function). The stack is a "LIFO" (last in, first out) data structure that is managed and optimized by the CPU quite closely. Every time a function declares a new variable, it is "pushed" onto the stack. Then every time a function exits, all of the variables pushed onto the stack by that function, are freed (that is to say, they are deleted). Once a stack variable is freed, that region of memory becomes available for other stack variables.

    So consider the following code sample:
    Code:
    #include <stdio.h>
    
    
    double multiplyByTwo (double input) 
    {
      double twice = input * 2.0;
      return twice;
    }
    
    
    int main (int argc, char *argv[])
    {
      int age = 30;
      double salary = 12345.67;
      double myList[3] = {1.2, 2.3, 3.4};
    
    
      printf("double your salary is %.3f\n", multiplyByTwo(salary));
    
    
      return 0;
    }
    How would the variables shown above be added to the stack and which is the last that goes? From what I know, the variables in int main are pushed on to the stack first since main() is where the program starts. But which variable does first, then is pushed out? Do the variables in multiplyByTwo() go last?

  2. #2
    Registered User
    Join Date
    Sep 2020
    Posts
    166
    The actual ordering doesn't matter, just as long as they are consistent. That is wath optimizing compilers do.

    For sample, as age and myList[] are not used, they won't even end up on the stack - the compiler will say "unused variable" and ignore them.

    But in general variables are added in the order they appear (or maybe more correctly enter and exit scope), and when making function calls parameters are pushed onto the working from first to last (i.e. the last parameter will be on the top of the stack).

    This is all a first approximation as the calling conventions for different CPUs and OSs specify that you pass the first few parameters and results using the CPU's internal registers.

  3. #3
    Registered User
    Join Date
    Feb 2019
    Posts
    785
    Unless you are compiling your code without any optimizations enabled, the compiler will use the stack to store local variables only if needed. In general, for optimized code, the compiler uses registers. Here's your example, compiled for x86-64:
    Code:
    ; 'twice' is eliminated!
    ; This function is here only because isn't 'static'.
    multiplyByTwo:
        addsd    xmm0, xmm0
        ret
    
    ; 'age' and 'mylist[]' are eliminated.
    ; main() never uses 'multiplyByTwo' function!
    main:
        sub    rsp, 8
        mov    edi, 1
        mov    eax, 1
        movsd    xmm0, QWORD PTR [.LC0]
        lea    rsi, [.LC1]
        call    __printf_chk@PLT
        xor    eax, eax
        add    rsp, 8
        ret
    
        section    .rodata
    .LC1:
      db  "double your salary is %.3f\n",0
    .LC0:
      ; The compiler knows it must multiply 12345.67 by 2, so it does at compile time.
      dq  24691.34
    Notice the compiler never alocated any of your 'local' variables on stack (and never called your multiplyByTwo() function on main()).

  4. #4
    Registered User
    Join Date
    May 2012
    Posts
    481
    The local variables in main are pushed first on to the stack. Notionally in the order in which they are declared, though the optimiser may make some changes and you can't rely on this if playing silly games with addresses and illegal accesses into conssecutive variables.

    Now we've got the parameter to the subroutine. Operating systems have what they call a "calling convention", which is fundamentally C-based but goes beyond C to apply to linkages in other languages. Normally this calling convention says that the first few arguments go in registers, the remaining ones on the stack. In this case, there's only one argument, so it will probably go in a register. But ona small processor, it might just be pushed on the stack.

    Then we call the subroutine. The variables local to the subroutine are puushed on the stack. Then the subroutine returns. The "pop" operation is usually a simple "decrement stack top" operation. All the local variables are popped in this one instruction.
    I'm the author of MiniBasic: How to write a script interpreter and Basic Algorithms
    Visit my website for lots of associated C programming resources.
    https://github.com/MalcolmMcLean


  5. #5
    Registered User
    Join Date
    Apr 2019
    Posts
    86

    Exclamation

    its two different animals compile for x86 or compile for x64 using very different calling conventions
    so x64 only uses stack in exceptions
    x86 calling conventions - Wikipedia
    you tell me you can C,why dont you C your own bugs?

  6. #6
    Registered User
    Join Date
    Feb 2019
    Posts
    785
    Quote Originally Posted by I C everything View Post
    its two different animals compile for x86 or compile for x64 using very different calling conventions
    so x64 only uses stack in exceptions
    x86 calling conventions - Wikipedia
    Yep... Try to compile the same code for i386 (or x86, as you called) and you'll get:
    Code:
      section .text
    
    _multiplyByTwo:
      fld QWORD [esp+4]
      fadd  st, st(0)
      ret
    
      section .rdata
    LC2:
      db `double your salary is %.3f\n`, 0
    
      section .text
    
      extern printf
    
      global _main
    _main:
      push  ebp
      mov   ebp, esp
      and   esp, -16
      sub   esp, 16
      call  ___main
      mov   DWORD [esp+4], -1030792151
      mov   DWORD [esp+8], 1087904981
      mov   DWORD [esp], .LC2
      call  _printf
      xor   eax, eax
      leave
      ret
    _multiplyByTwo isn't called by _main and the direct doubled value is pushed before printf. No 'local vars' are pushed!

    Let's try with ARM AArch32, shall we?
    Code:
      .cpu cortex-a53
      .arm
      .fpu vfp
    
      .section .text
    
      .extern printf
    
    multiplyByTwo:
      vadd.f64  d0, d0, d0
      bx        lr
    
      .global  main
    main:
      push  {r4, lr}
      movw  r0, #:lower16:.LC1
      movt  r0, #:upper16:.LC1
      movw  r2, #23593
      movt  r2, 49807
      movw  r3, #7381
      movt  r3, 16600
      bl    printf  
      mov   r0, #0  
      pop   {r4, pc}
    
      .section  .rodata 
    .LC1:
      .ascii  "double your salary is %.3f\012\000"
    Still... no multiplyByTwo call and still no 'local var' pushing...
    Last edited by flp1969; 12-03-2020 at 06:31 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. address of stack variables
    By telmo_d in forum C Programming
    Replies: 4
    Last Post: 04-06-2016, 03:48 PM
  2. local variables and stack
    By telmo_d in forum C Programming
    Replies: 1
    Last Post: 06-04-2015, 02:14 PM
  3. local stack variables
    By Amyaayaa in forum C++ Programming
    Replies: 25
    Last Post: 10-07-2008, 01:13 PM
  4. how to identify which button is pushed
    By gaurav07 in forum Windows Programming
    Replies: 1
    Last Post: 06-23-2005, 10:44 AM
  5. Doing stuff when certain Key combo's pushed
    By HisWord in forum Windows Programming
    Replies: 13
    Last Post: 09-30-2001, 07:39 AM

Tags for this Thread