If the stack grows at numerically lower address why does pointer comparison reverses this?

pointers in c
what is pointer
pointers and addresses
print pointer value c
pointer assignment in c
mips assembly language programming examples
c get value from pointer
how to print a pointer in c++

Since the stack grows downwards, ie towards numerically smaller memory addresses why does &i < &j is true. Correct me if I'm wrong, but I'd imagine this was a design decision of C creators (that C++ maintains). But I wonder why though.

It is also strange that a heap-allocated object pin lies at numerically higher memory address than a stack variable and this also contradicts the fact that the heap lies at numerically smaller memory addresses than the stack (and increases upwards).

#include <iostream>

int main()
{
    int i = 5;                  // stack allocated
    int j = 2;                  // stack allocated
    int *pi = &i;               // stack allocated
    int *pj = &j;               // stack allocated

    std::cout << std::boolalpha << '\n';
    std::cout << (&i < &j) && (pi < pj) << '\n';            // true
    struct S
    {
        int in;
    };
    S *pin                      // stack allocated
        = new S{10};            // heap allocated
    std::cout << '\n' << (&(pin->in) > &i) << '\n';         // true
    std::cout << ((void*)pin > (void*)pi) << '\n';          // true
}

Am I right so far and if so why C designers reversed this situation that numerically smaller memory addresses appear higher (at least when you compare the pointers or through the addressof operator &). Was this done just 'to make things work'?

Correct me if I'm wrong, but I'd imagine this was a design decision of C creators

It is not part of the design of the C language, nor C++. In fact, there is no such thing as "heap" or "stack" memory recognised by these standards.

It is an implementation detail. Each implementation of each language may do this differently.


Ordered comparisons between pointers to unrelated objects such as &i < &j or (void*)pin > (void*)pi have an unspecified result. Neither is guaranteed to be less or greater than the other.

For what it's worth, your example program outputs three counts of "false" on my system.

Does stack grow upward or downward?, How do you know which direction a memory stack grows in? Stack-machine code, a form of one-address code, assumes the presence of a stack of operands. Most operations take their operands from the stack and push their results back onto the stack. For example, an integer subtract operation would remove the top two elements from the stack and push their difference onto the stack.

The compiler generated code that isn't allocating space for each individual variable in order, but allocating a block for those local variables, and thus can arrange them within that block however it chooses.

Why does the stack address grow towards decreasing memory , When two different pointers are pointing same elements is called? Register variables act as auto variables, except they do not have an "address", and so cannot be referred to by pointer variables or by the address operator, &. Loop counters are the most common and obvious use of register variables.

Usually, all the local variables of one function are allocated as one block, during function entry. Therefore you will only see the stack growing downward if you compare the address of a local variable allocated in an outer function with the address of a local variable allocated in an inner function.

How to compare pointers?, To initialize a pointer variable, you have to assign to it the address of Local variables a and p are allocated on the stack, which grows down from this is in the code segment, which is a bit lower in memory than all the global The result is an integer value, equal to the numerical difference between the addresses divided  Returning a pointer to a local variable. Returning a pointer to the local variable output is incorrect. It may happen to work but it is wrong to do so, because the storage for that local variable goes out of scope the moment you return from the function. To be correct, you should either malloc a buffer and return it (preferred) or make output

It's really rather easy: such a stack is an implementation detail. The C and C++ language spec doesn't even need to refer to it. A conforming C or C++ implementation does not need to use a stack! And if it does use a stack, still the language spec doesn't guarantee that the addresses on it are allocated in any particular pattern.

Finally, the variables may be stored in registers, or as immediate values in the code text, and not in data memory. Then: taking the address of such a variable is a self-fulfilling prophecy: the language spec forces the value to a memory location, and the address of that is provided to you - this usually wrecks performance, so don't take addresses of things you don't need to know the address of.

A simple cross-platform example (it does the right thing on both gcc and msvc).

#ifdef _WIN32
#define __attribute__(a)
#else
#define __stdcall
#endif

#ifdef __cplusplus
extern "C" {
#endif
__attribute__((stdcall)) void __stdcall other(int);

void test(){
    int x = 7; 
    other(x);
    int z = 8;
    other(z);
}

#ifdef __cplusplus
}
#endif

Any reasonable compiler won't put x nor z in memory unnecessarily. They will be either stored in registers, or will be pushed onto the stack as immediate values.

Here's x86-64 output from gcc 9.2 - note that no memory loads nor stores are present, and there's tail call optimization!

gcc -m64 -Os

test:
        push    rax
        mov     edi, 7
        call    other
        mov     edi, 8
        pop     rdx
        jmp     other

On x86, we can force a stdcall calling convention that uses stack to pass all parameters: even then, the value 7 and 8 is never in a stack location for a variable. It is pushed directly to the stack when other gets called, and it doesn't exist on the stack beforehand:

gcc -m32 -fomit-frame-pointer -Os

test:
        sub     esp, 24
        push    7
        call    other
        push    8
        call    other
        add     esp, 24
        ret

How does pointer comparison work in C? Is it ok to compare , The convention of making stacks grow down comes from the era before that most occur "on high addresses": the buffer has size 1000 but the buggy Reversing the stack direction just swaps the roles: now, low overflow become critical. Notice that the stack frame for strcpy will sit numerically above the  From time to time I have had to do numerical work in MATLAB and the 1-based indexing always stuck out like a sore thumb, so I have a few examples I can provide where 0-based indexing is advantageous. Modular access. This is the simplest example. If you want to "wrap around" an array, the modulus operator works like a charm.

C/Pointers, Most also assign a numeric address to each byte. The condition is always a comparison of 2 registers, or a register and a constant. If a jump (or branch) reloads the PC with a lower address, that instruction will be reached again, and again. then the stack pointer is incremented, exactly reversing the push operation. ESP (the stack pointer) is decremented by push since the x86 stack grows down - i.e. the stack grows from high addresses to lower addresses. Syntax push <reg32> push <mem> push <con32> Examples push eax — push eax on the stack push [var] — push the 4 bytes at address var onto the stack. pop — Pop stack

Inversed Data Direction on the Stack, At the time of Go's inception, only a decade ago, the programming world was To meet these goals required addressing a number of linguistic issues: an it isn​'t, the run-time grows (and shrinks) the memory for storing the stack automatically, If we store a nil pointer of type *int inside an interface value, the inner type will  But in situations where you have to do tree traversal and you have to be as fast as possible, trading the memory for the parent pointer (which probably fits within the same cache line as the rest of the node anyhow, so in practice basically free) vs recursion or an explicit stack which stress memory in the tight loop, it is probably a winner!

CA225b MIPS Assembly Language Programming, is added. Figure 4.39 shows that the user stack grows upward in main memory start- less than the address of the item that was on the top of the stack. You can The first action sets the stack pointer to the content of memory location FFF8. Unlike Figure 6.6, the if statement in Figure 6.8 does not compare a variable's. 3. While i ≥ 0 do: (a) If D ≥ 2i, then • Set X i = 1 (i.e. set bit i of X to 1). • Set D = (D −2i). (b) Set i = (i−1). 1.1.1.3 Addition of Unsigned Binary Numbers Addition of binary numbers can be done in exactly the same way as addition of decimal numbers, except that all of the operations are done in binary (base 2) rather

Comments
  • It's Unspecified Behavior to compare the address of elements that are not part of the same array, other than for (in)equality. The result is determined by your platform/implementation.
  • Try comparing the addresses of variables in two different stack frames, e.g. pass pj into a function an compare it to the address of a local variable in that function
  • Mandatory linked Q&A stackoverflow.com/questions/31774683/…
  • @FrançoisAndrieux Is it really undefined, or just meaningless? I'd think that you could compare pointers just fine, but since the placement of variables in memory is entirely the compiler's business, your program shouldn't care what the result of most comparisons is.
  • @Caleb The standard explicitly says it's unspecified. Each implementation defines it's own rules, but they need to define some rules. You must have caught my comment between edits.
  • What system is that?
  • @Nikos Linux stacked-crooked 4.4.0-57-generic #78-Ubuntu SMP Fri Dec 9 23:50:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux with compiler g++ (GCC) 9.2.0
  • Ok. I just checked a Kali 4.15 x64 system with g++ 7.3.0 and it outputs true, false, false.