0

Several years ago, I put together this little memory address exploration for my students to help them to understand pointers, arrays, the stack, and the heap.

I just compiled and ran it in a new environment (gcc on a AWS Linux server) and the order of the parameters for foo are different from what I would expect. The local function variables (d1 and e1) now have a higher address in comparison to the function parameters (a1, b1, and c1).

The addresses for the parameters / variables in function foo are listing as:

&a1: fa2740fc
&b1: fa2740f0
&c1: fa2740e8
&d1: fa27410c
&e1: fa274100

Any thoughts on why variables d1 and e1 have higher addresses than a1, b1, and c1?

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

int* foo (int a1, int b1[], int *c1)
{
  int d1 = *c1 + a1;
  int *e1;
  
  e1 = malloc (sizeof(int));

  printf ("5) Addresses of arguments/variables of foo:\n");
  printf ("   Update your stack sketch.\n");
  printf ("     &a1: %x\n", &a1);
  printf ("     &b1: %x\n", &b1);
  printf ("     &c1: %x\n", &c1);
  printf ("     &d1: %x\n", &d1);
  printf ("     &e1: %x\n\n", &e1);

  printf ("6) Values of arguments/variables in foo:\n");
  printf ("   Include these on your stack sketch as well.\n");
  printf ("     a1: %x\n", a1);
  printf ("     b1: %x\n", b1);
  printf ("     c1: %x\n", c1);
  printf ("     d1: %x\n", d1);
  printf ("     e1: %08x\n\n", e1);

  printf ("7) *c1 == %x, why?  Explain using your stack drawing.\n\n", *c1); 
  
  printf ("8) e1 is a reference to an integer, much like c1.  Why is e1 so\n ");
  printf ("   different in value?\n\n");

  return e1;
}


int main ()
{
  int a = 5;
  int b[] = {8, 14, -7, 128, 12};
  int c = 10;
  int d = 14;

  printf ("1) Locations...\n");
  printf ("   Use these locations to sketch the stack.\n");
  printf ("     &a: %x\n", &a);
  printf ("     &b: %x\n", &b);
  printf ("     &c: %x\n", &c);
  printf ("     &d: %x\n\n", &d);
  
  printf ("2a) Values:\n");
  printf ("   Why does b != 8?\n");
  printf ("     a: %x\n", a);
  printf ("     b: %x\n", b);
  printf ("     c: %x\n", c);
  printf ("     d: %x\n\n", d);

  printf ("2b) Values:\n");
  printf ("   What memory address is *b accessing?\n");
  printf ("     *b: %x\n\n", *b);
  
  printf ("3) Notice that the following increase by 4 each, why?\n");
  printf ("     &(b[0]): %x\n", &(b[0]));
  printf ("     &(b[1]): %x\n", &(b[1]));
  printf ("     &(b[2]): %x\n", &(b[2]));
  printf ("     &(b[3]): %x\n\n", &(b[3]));

  printf ("4) Pointers can be added, but the addition might have interesting results.\n");
  printf ("   Explain why b + 1 != b + 1 in the normal way of thinking about addition.\n");
  printf ("     b:   %x\n", b);
  printf ("     b+1: %x\n", b+1);
  printf ("     b+2: %x\n", b+2);
  printf ("     b+3: %x\n\n", b+3);
  
  foo (a, b, &c);
}
Bryan R
  • 111
  • 2
  • What compiler and OS was the code originally written for? – trent Feb 04 '21 at 17:56
  • https://stackoverflow.com/questions/1677415/does-stack-grow-upward-or-downward this link may help you. – Sasi V Feb 04 '21 at 18:01
  • 1
    Does this answer your question? [Printf arguments not pushed on the stack](https://stackoverflow.com/questions/19351451/printf-arguments-not-pushed-on-the-stack) – trent Feb 04 '21 at 18:17
  • 1
    tl;dr: x86_64 calling conventions pass integer and pointer arguments in registers, not on the stack. – trent Feb 04 '21 at 18:17
  • I previously ran it on cygwin. The current environment is AWS. uname reports x86_64. This is more of a head scratcher than anything else. I already played with the pass by register question by adding 4 dummy parameters, with no real change. I will play with this possibility some more. The stack is growing downward as is typical for the x86. But, I am used to the incoming parameters being at an address higher than the base pointer and the local variables at an address lower than the base pointer. Apparently, this varies by environment. Kinda surprised me. – Bryan R Feb 04 '21 at 18:24
  • 1
    I hope you're teaching your students that anything they learn from this exercise is merely how it *might be* implemented -- and, I agree, having a concrete example helps a lot. But they obviously shouldn't get the impression that this is the way it *must* be, nor should they write code that depends on it, nor should they get their noses out of joint if one day they encounter a machine or compiler that behaves strangely differently. – Steve Summit Feb 04 '21 at 19:06
  • Certainly -- it is simply an exercise to develop an understanding of pointers, passing shared references, the stack vs the heap, and such. Getting into the precise details is beyond the scope of the lesson but something that might come up in discussions with the class. – Bryan R Feb 04 '21 at 19:15

1 Answers1

1

As @trentcl commented, apparently the parameters a1, b1, and c1 are being passed via registers and not on the stack. Editing foo to take an additional 8 dummy parameters forces a1, b1, and c1 to be passed as parameters on the stack with an address higher than the local parameters.

Bryan R
  • 111
  • 2