Home > OS >  I am new to C and I have a question about pointers
I am new to C and I have a question about pointers

Time:09-23

The following lines of code work as you'd expect

#include <stdio.h>

int main(void)
{
  int n;
  int a[5];
  int *p;

  a[2] = 1024;
  p = &n;
  /*
   * write your line of code here...
   * Remember:
   * - you are not allowed to use a
   * - you are not allowed to modify p
   * - only one statement
   * - you are not allowed to code anything else than this line of code
   */
  
  /* ...so that this prints 98\n */
  printf("a[2] = %d\n", a[2]);
  return (0);
}

This prints out a[2] = 1024

Now I was asked to modify this code so that a[2] = 98 gets printed instead. There were a ton of constraints. I couldn't use the variable a anywhere else in the code again and a couple other things. I found a solution online but I don't understand it at all.

#include <stdio.h>

int main(void)
{
  int n;
  int a[5];
  int *p;

  a[2] = 1024;
  p = &n;
  /*
   * write your line of code here...
   * Remember:
   * - you are not allowed to use a
   * - you are not allowed to modify p
   * - only one statement
   * - you are not allowed to code anything else than this line of code
   */
  p[5] = 98;
  /* ...so that this prints 98\n */
  printf("a[2] = %d\n", a[2]);
  return (0);
}

So, setting p[5] = 98; results in a[2] = 98 being printed, which is the intended result. I'm fairly new to C programming and pointers in general but I have absolutely no idea why this works the way it does.

CodePudding user response:

This is all about the layout of the stack, the memory where the local variables in your method are stored. Variables are pushed onto the stack, so first the variable n is pushed to the stack, let us assume that it ends up at address 1000 (this is just a fictional address). Since it is an integer it takes up the space of an integer (4 bytes if integers are 32 bits). Then you push the array a to the stack. It will be located next to n.

Since n was placed at address 1000 and took up 4 bytes of memory, then a will be placed at address 1004 (1000 4). a is an array of 5 integers, each taking up 4 bytes. So a takes up the space from 1004 to 1024.

Your variable p is an integer pointer, and you set it to point to n. That means that p points to the address 1000 which is the address of n. You then write p[5] in C that is equivalent to the expression *(p 5). Which basically means take value of the address p 5. And since p is an integer pointer and each integer takes up 4 bytes, you are essentially asking for the value of address: 1000 (5 * 4) = 1020

In the array a you stored the value 1024 at index 2, that corresponds to address: 1004 2 * 4 = 1012, so when you print the value of a[2] you are printing the value of address 1012. This means that the value you are setting to 98, is not a[2] but a[4].

The reason why I am mentioning this, is that in my case it did not print 98 but 1024. As people have already mentioned you are working with undefined behavior, and although it might work on some setups, it might not work on all.

CodePudding user response:

At very first be aware that accessing arrays out of their bounds (like a[-1], a[1012]) is undefined behaviour, in other words: illegal. A (partial) exception is the one past the end pointer, in this case &a[5] or simply a 5 – it is a valid pointer, but not dereferencible (doing so then again would be undefined behaviour).

A single object of any type counts as an array of length 1 (here &n 1 would result in the one-past-the-end pointer, again valid but not dereferencible), thus p[5], as pointing to single object n, is undefined behaviour as well.

So as now outside of the legal world of C we can still peek under the hoods and look why we are getting the desired result.

At first you need to know that p[5] is only syntactic sugar and equivalent to *(p 5) – meaning that you add to the address stored in p – the one of n – a value of five times the size of int, and then access this address.

Apparently by advancing five ints from n you receive the address of a[2], and thus you could modify the array at this location.

This let's deduce how memory has been arranged; we can now deduce that you reach a[0] – and thus a itself – by advancing three ints, which means that

  1. n must have a lower address than a, thus indicating a bottom up stack, and
  2. there must be a gap in between n and a in the size of two ints.

Be aware, though, that depending entirely on hardware and especially operating system. There's absolutely no guarantee that this would work with the same values on another system – with e.g. a top-down stack n would have been placed at a higher address than a and you would have needed a negative offset!

  • Related