1

I came across this question on SO: (Tricky pointer question):

A C programmer is working with a little-endian machine with 8 bits in a byte and 4 bytes in a word. The compiler supports unaligned access and uses 1, 2 and 4 bytes to store char, short and int respectively. The programmer writes the following definitions (below right) to access values in main memory (below left):

Address | Byte offset

--------|-0--1--2--3----

   0x04 | 10 00 00 00

   0x08 | 61 72 62 33

   0x0c | 33 00 00 00

   0x10 | 78 0c 00 00

   0x14 | 08 00 00 00

   0x18 | 01 00 4c 03

   0x1c | 18 00 00 00


int **i=(int **)0x04;  
short **pps=(short **)0x1c;  

struct i2c {  
int i;  
char *c;  
}*p=(struct i2c*)0x10;

(a) Write down the values for the following C expressions:

**i  
p->c[2]  
&(*pps)[1]  
++p->i 

That question only answers the third subquestion but I was wondering how the rest of the subquestions would be solved. I'm new to C and trying to improve my understanding on pointers and this was particularly confusing. Thanks for any help!

Community
  • 1
  • 1
user2997582
  • 67
  • 1
  • 9

1 Answers1

3

You'll probably want to refer to this: Operators in C and C++ - Operator precedence

The question didn't specify, but we will assume that pointers are 4 bytes - sizeof(void*) == 4.


1.

int **i=(int **)0x04;
**i = ???

i is a pointer to a (pointer to int). When we say **i, we are de-referencing the pointer twice - or reading the value that the pointer points to.

First note that **i == *(*i). So we first read a pointer-sized (4-byte) value from memory at address 0x04. This is 10 00 00 00 - interpreted as a little-endian value, that is 0x10. So now we are left with *((int*)0x10). That means we read an int-sized (4-byte) value from memory at address 0x10. 78 0c 00 00 interpretted in little-endian is the value 0xC78.


2.

struct i2c {  
    int i;  
    char *c;  
} *p = (struct i2c*)0x10;

p->c[2] = ???

This one's a little trickier. I'll assume that you understand that structures are just a collection of variables that (excluding padding, which doesn't apply here) are laid out one after another in memory.

Our pointer p points to a struct i2c object at 0x10 in memory. That means at address 0x10 is the int, named p->i. And immediately following that, at address 0x14, is the char *, named p->c.

The expression p->c[2] means: "First get the char *c from the structure that p points to. Then, get the char at index 2 from the array that p->c points to."

So first, we'll get p->c. I already mentioned this char* is at address 0x14. There we find the pointer 08 00 00 00, or 0x8.

Now, we have a char * that points to address 0x8, and we want the char at index 2 in that array. To get the address of an array element, we use this formula:

&(x[y])  ==  (char*)x + (y * sizeof(x[0]))

In other words, the offset (from the start of the array) of the nth element in the array is n times the size of each element.

Since chars are 1 byte, p->c[2] is at 0x8 + 2 = 0xA. There we find the value 0x62, which is the ASCII character 'b'.


3.

short **pps=(short **)0x1c;

&(*pps)[1] = ???

With our knowledge operator precedence, we read &(*pps)[1] as "First dereference pps, which is a pointer-to-short (or an array of shorts). Then, we want the address of the element at index 1."

At address 0x1C we have 18 00 00 00, or 0x18. So now we have a pointer to an array of shorts, and this array starts at address 0x18. Using our formula from above, and knowing that shorts are 2 bytes in size, we calculate element 1 to be at address 0x18 + (1 * 2) == 0x1A.

At address 0x1A is 4c 03, or 0x034C. However, the problem wasn't asking us for the value of element 1 - that would be solving (*pps)[1]. Instead, it asked for &(*pps)[1] or the address of that element. So, we simply go back to the end of the previous paragraph, where we said the address was 0x1A.


4.

++p->i = ??

For this one, you really need to know the operator precedence. It should be clear that this could be interpreted two different ways:

  • a) Increment the pointer p 1, and then dereference p to get its member i
  • b) Dereference p to get its member i, then increment the integer value

From the precedence chart, we see that -> has a precedence of 2, while ++ (the Prefix increment), has a lower precedence of 3. That means we need to apply the -> first, then increment. Thus, option b) was correct.

So, first let's get p->i. We already said in part 2. that since p points to address 0x10, and i is the first member in struct i2c, p->i is at address 0x10. There we find 78 0c 00 00, or 0xC78.

Finally, we need to apply the ++ operator, and increment that value to 0xC79.

.....

1 - Pointer arithmetic means you treat a pointer like an array. So p + 3 doesn't mean "p plus 3 bytes", it means &p[3], or "p plus (3 * sizeof(*p)) bytes".

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328