6

I'm trying to understand how C allocates memory on stack. I always thought variables on stack could be depicted like structs member variables, they occupy successive, contiguous bytes block within the Stack. To help illustrate this issue I found somewhere, I created this small program which reproduced the phenomenon.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void function(int  *i) {
    int *_prev_int =  (int *) ((long unsigned int) i -  sizeof(int))  ;
    printf("%d\n", *_prev_int );    
}

void main(void) 
{
    int x = 152;
    int y = 234;
    function(&y);
}

See what I'm doing? Suppose sizeof(int) is 4: I'm looking 4 bytes behind the passed pointer, as that would read the 4 bytes before where int y in the caller's stack.

It did not print the 152. Strangely when I look at the next 4 bytes:

int *_prev_int =  (int *) ((long unsigned int) i +  sizeof(int))  ;

and now it works, prints whatever in x inside the caller's stack. Why x has a lower address than y? Are stack variables stored upside down?

Arun A S
  • 6,421
  • 4
  • 29
  • 43
doc_id
  • 1,363
  • 13
  • 41
  • stack is stored downwards – unbesiegbar Feb 17 '15 at 09:50
  • 2
    I think it is implementation defined/ or unspecified. Check this answer http://stackoverflow.com/a/4105123/1673391 – Grijesh Chauhan Feb 17 '15 at 09:50
  • 3
    This is completely platform-dependent, but many mainstream platforms actually grow the heap upward from the code / data section and the stack downward from the top of (available) memory. You should **never** rely on that in your C code, though... – DevSolar Feb 17 '15 at 09:51
  • 1
    Is `int* prev = i - 1;` too simple for the same effect? – mch Feb 17 '15 at 09:55
  • Are asking about Intel x86/x64 only? – sashoalm Feb 17 '15 at 09:55
  • @mch sure that would do it but here I'm trying to understand how things work under the hood – doc_id Feb 17 '15 at 09:57
  • @sashoalm No, I just never thought of a stack other than what "stack" means in English as successive set of elements. I naively thought that was an obvious standard. – doc_id Feb 17 '15 at 10:01
  • On Intel, stack will (should) always be downward because asm instructions like PUSH/POP, CALL/RET use a downward stack. – ElderBug Feb 17 '15 at 10:03
  • @rahmanisback If you chose a particular architecture (like Intel or ARM), we could give you a concrete answer. It might be downward on Intel, but upward on ARM, for example (note: I don't really know how it is in ARM). So you should change your question to "Is the stack direction defined by the C standard" (and the answer is "no"), or "What is the stack direction on Intel x86?". – sashoalm Feb 17 '15 at 10:06
  • There is no requirement in C stating that a stack must be present. I have once done a little C project on a minimalistic MCU that didn't even have a stack pointer. – Lundin Feb 17 '15 at 10:07
  • Actually, gcc basically first allocates all space needed on the stack (which usually grows downwards), acquires a pointer to the lowest address (SP *after* the allocation) and then uses this pointer like a pointer to a `struct` holding any variables and/or parameters to access the values. Where in this stack space which variable resides really just depends on the compiler (and target architecture) and should be documented with the compiler. – JimmyB Feb 17 '15 at 10:08
  • 1
    C doesn't allocate anything. It is a *specification*. And it says absolutely nothing about what you are trying to check. Which means you cannot rely on the results you are getting from your experiment in any way. Or indeed assign any meaning to them. – n. m. could be an AI Feb 17 '15 at 10:08
  • Just FYI, Only pointer to y is stored into stack during function call. X and y is stored into stack even earlier, during main call. And you could check how they allocated just by comparing their addresses without redundant function call. – Evgen Bodunov Feb 17 '15 at 10:16
  • 1
    To see why your experiment makes little sense, look [here](http://ideone.com/F0APII). – n. m. could be an AI Feb 17 '15 at 10:22

3 Answers3

11

Stack organization is completely unspecified and is implementation specific. In practice, it depends a lot of the compiler (even of its version) and of optimization flags.

Some variables don't even sit on the stack (e.g. because they are just kept inside some registers, or because the compiler optimized them -e.g. by inlining, constant folding, etc..).

BTW, you could have some hypothetical C implementation which does not use any stack (even if I cannot name such implementation).

To understand more about stacks:

Sadly, I know no low-level language (like C, D, Rust, C++, Go, ...) where the call stack is accessible at the language level. This is why coding a garbage collector for C is difficult (since GC-s need to scan the call stack pointers)... But see Boehm's conservative GC for a very practical and pragmatic solution.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • it need to quote the standard for such assertion/comments – Grijesh Chauhan Feb 17 '15 at 09:56
  • 4
    AFAIK, the C99 standard does not mention the/any stack in a normative way. – Basile Starynkevitch Feb 17 '15 at 09:57
  • 1
    @GrijeshChauhan: No it doesn't need quotes from the standard, because it is **unspecified** by the standard. That is why you shouldn't rely on its behaviour. – DevSolar Feb 17 '15 at 09:58
  • Re: "hypothetical C implementation" - I remember there was some tweak for gcc to compile for lowest-end AVR processors which have no RAM (or stack pointer) but only 32x8 bit registers. – JimmyB Feb 17 '15 at 10:01
  • @BasileStarynkevitch Check if [3.4.1](https://is.muni.cz/www/408176/38744863/International_Standard_for_C.txt) or [3.4.4](https://is.muni.cz/www/408176/38744863/International_Standard_for_C.txt)can be helpful from N1570 Committee Draft - I am not sure but – Grijesh Chauhan Feb 17 '15 at 10:02
  • 1
    @GrijeshChauhan: Since you have a text document there, search for "stack" and see how it does not give a single match. Where parameters and local variables are stored is not defined by the standard -- i.e. **unspecified behaviour**. Maybe your platform or compiler documents how it does things, that would mean it is **implementation-defined**. – DevSolar Feb 17 '15 at 10:03
  • @DevSolar Yes I did it, their is not match you are correct, anyways Basile Starynkevitch now added good details about it. – Grijesh Chauhan Feb 17 '15 at 10:05
  • 2
    @GrijeshChauhan: Actually I believe adding such information does more harm than good, because too many programmers do not understand the implications of relying on unspecified / implementation-defined behaviour, and happily use whatever "works for me", then wonder why the next compiler or OS update breaks their code. – DevSolar Feb 17 '15 at 10:08
  • @DevSolar your view is correct actually - it also took time to me to understand such concepts -- even I was average in programming. – Grijesh Chauhan Feb 17 '15 at 10:16
3

Almost all the processors architectures nowadays supports stack manipulation instruction (e.g LDM,STM instructions in ARM). Compilers with the help of those implements stack. In most of the cases when data is pushed into stack, stack pointer decrements (Growing Downwards) and Increments when data popped from stack.

So it depends on processor architecture and compiler how stack is implemented.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Vagish
  • 2,520
  • 19
  • 32
1

Depends on the compiler and platform. The same thing can be done in more than one way as long it is done consistently by a program (this case the compiler translation to assembly, i.e. machine code) and the platform supports it (good compilers try to optimize assembly to get the “most” of each platform).

A very good source to deeply understand what goes behind the scenes of c, what happens when compiling a program and why they happen, is the free book Reverse Engineering for Beginners (Understanding Assembly Language) by Dennis Yurichev, the latest version can be found at his site.

abetancort
  • 424
  • 5
  • 10