1
#include<stdlib.h>

int main()
{
   char ch, *p1, **p2, ***p3, ****p4;
   ch='a';
   p1=&ch;
   printf("%c %d %c\n", ch, p1, *p1);

   p2=&p1;
   printf("%d %d %c\n", p2, *p2, **p2);

   p3=&p2;
   printf("%d %d %d %c\n", p3, *p3, **p3, ***p3);

   p4=&p3;
   printf("%d %d %d %d %c\n", p4, *p4, **p4, ***p4, ****p4);
}

The output looks like :

a 298923415 a                                                                                                                 
298923416 298923415 a                                                                                                          
298923424 298923416 298923415 a                                                                                                
298923432 298923424 298923416 298923415 a           

Why is it that for p1 and p2 the address assigned is in increment of 1 and for p3 and p4 the address assigned is in increment of 8?

Do the addressing follow any pattern as they are assigned in continuous memory location?

Blaze
  • 16,736
  • 2
  • 25
  • 44
  • 1
    the best way to tell is to check your compiler documentation. – Sourav Ghosh Oct 08 '18 at 08:10
  • 5
    For starters, print pointers with `%p`, not `%d`. Just saying. – WhozCraig Oct 08 '18 at 08:10
  • The compiler can do whatever it wants, but usually they should all be next to each other on the stack, barring some space between them due to aligning. Of course, the language gives no guarantee whatsoever, but perhaps your compiler does. – Blaze Oct 08 '18 at 08:10
  • I just used %d for simplicity sake! ;) @WhozCraig –  Oct 08 '18 at 08:15
  • Ok... Thank you:) @Blaze –  Oct 08 '18 at 08:17
  • @Jach because `%p` is more "complex" to type? Simple or not, you're lying to `printf`, sending it a pointer when it expects `int`. They're not synonymous, and compiling on x64 with most toolchains will amplify the mistake. Just use what you're supposed to as a habit. – WhozCraig Oct 08 '18 at 08:27
  • 1
    if I point out that it corresponds to the `sizeof` of the pointed-to type (`sizeof(char)` vs. `sizeof(char*)`), laid out consecutively in memory, does that make things clearer ? As said before, this only explains your specific use case in your specific environment - this is not guaranteed to always be so. – Sander De Dycker Oct 08 '18 at 08:28
  • If you use `%d` to print pointers on a 64 bit big endian system, you would get nonsense output, namely 4 identical addresses. (I'm not sure if any such computers yet exist though.) – Lundin Oct 08 '18 at 08:44

3 Answers3

4

Your objects are laid out sequentially in memory in this case. They don't necessarily have to be though. The compiler can lay things out in memory however it wants.

In your case, your memory looks something like this:

298923415 +-----------+
          |    'a'    | ch
298923416 +-----------+
          |           |
          |           |
          |           |
          | 298923415 | p1
          |           |
          |           |
          |           |
          |           |
298923124 +-----------+
          |           |
          |           |
          |           |
          | 298923416 | p2
          |           |
          |           |
          |           |
          |           |
298923132 +-----------+
          |           |
          |           |
          |           |
          | 298923424 | p3
          |           |
          |           |
          |           |
          |           |
298923140 +-----------+
          |           |
          |           |
          |           |
          | 298923432 | p4
          |           |
          |           |
          |           |
          |           |
          +-----------+

Remember that, on most modern systems, pointer-to-object types are just integers whose job is to store a memory address. On your system (I'm guessing x86_64) pointers have a size of 8 bytes. This is totally system-dependent though. For example, a pointer is 4 bytes on 32-bit x86 systems, and there are plenty of other platforms with exotic pointer sizes.

Also the size of a char is defined to be 1, so the first pointer sits one byte after ch.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
2

What happens depends on what the compiler does. There's no guarantee in the language itself, but usually they get placed close to each other on the stack. There's a nice example of how it works on x86-64 (what your PC probably has) here:

The example uses the following code:

long myfunc(long a, long b, long c, long d,
        long e, long f, long g, long h)
{
    long xx = a * b * c * d * e * f * g * h;
    long yy = a + b + c + d + e + f + g + h;
    long zz = utilfunc(xx, yy, xx % yy);
    return zz + 20;
}

Stack Frame Layout on x86-64

Relevant to your question is the xx yy zz part. Like in your question, those are variables declared on the stack. As you can see on the image, they got placed next to each other. xx has the highest address, then comes yy and then zz. To illustrate this, you could do (&xx)[-1] and might get the value of yy. Note that this is undefined behavior and only works because we know what the compiler does with it, in any different setup it might not work, could cause bugs and crash the program (or worse).

Also, consider this post about memory alignment, which may cause gaps between your data in order to make it run faster.

Blaze
  • 16,736
  • 2
  • 25
  • 44
1

You are reading your output the wrong way. ch is allocated at address 298923415. The pointer p1 is allocated at the next byte, 298923416. From there on, each pointer is allocated by a multiple of 8 bytes, suggesting a 64 bit system.

This simply means that the compiler allocates char differently than pointers. This is because char is 1 byte and has no alignment requirements, so it may be placed at a misaligned address.

Generally, the compiler is free to allocate variables where it likes and you have no guarantees. And there is no point in discussing stack memory layout without a specific system in mind.

Fixing your example into something clearer, the results are unspectacular:

#include <stdio.h>

int main (void)
{
   char ch = 'a';
   char*    p1 = &ch;
   char**   p2 = &p1; 
   char***  p3 = &p2;
   char**** p4 = &p3;
   printf("ch:\t%p\n", (void*)p1);
   printf("p1:\t%p\n", (void*)p2);
   printf("p2:\t%p\n", (void*)p3);
   printf("p3:\t%p\n", (void*)p4);
   printf("p4:\t%p\n", (void*)&p4);
}

Output (x64 Windows PC, hex addresses):

ch:     000000000022FE4F
p1:     000000000022FE40
p2:     000000000022FE38
p3:     000000000022FE30
p4:     000000000022FE28
Lundin
  • 195,001
  • 40
  • 254
  • 396