I am trying to learn pointers in C, and taking a quiz for this purpose. Here is the question:
#include <stdio.h>
char *c[] = {"GeksQuiz", "MCQ", "TEST", "QUIZ"};
char **cp[] = {c+3, c+2, c+1, c};
char ***cpp = cp;
int main()
{
printf("%s ", **++cpp);
printf("%s ", *--*++cpp+3);
printf("%s ", *cpp[-2]+3);
printf("%s ", cpp[-1][-1]+1);
return 0;
}
The result of the line:
printf("%s ", *cpp[-2]+3);
confuses me, but let me explain step by step, how I understand that.
char *c[]
- is array of pointers to char.char **cp[]
- is array of pointers that points to pointer to char (I consider this as a wrapper for *c[]
in reverse order).char ***cpp
- is a pointer to pointer that points to pointer to char (I consider this a wrapper for **cp[]
to perform on place modifications).**++cpp
- since cpp
points to cp
, then ++cpp
will point to cp+1
which is c+2
, so double dereference will print TEST
.
*--*++cpp+3
- since now cpp
points to cp+1
, then ++cpp
will point to cp+2
which is c+1
, and the next operation --
will give us pointer to c
, so the last dereference will print sQuiz
.
Here comes confusion:
cpp[-2]
- since now cpp
points to cp+2
, which I can confirm with
printf("%p\n", cpp); // 0x601090
printf("%p\n", cp+2); // 0x601090
here I print addresses of pointers in c
printf("c - %p\n", c); // c - 0x601060
printf("c+1 - %p\n", c+1); // c+1 - 0x601068
printf("c+2 - %p\n", c+2); // c+2 - 0x601070
printf("c+3 - %p\n", c+3); // c+3 - 0x601078
so when I dereference like this *(cpp[0])
or **cpp
I expectedly get the value MCQ
of c+1
printf("%p\n", &*(cpp[0])); // 0x601068
but when I say *(cpp[-2])
I get QUIZ
, but I would rather expect to get some garbage value.
So my questions are:
How the magic with *--*++cpp+3
works, I mean what is modified by the --
part that allows me to get MCQ
instead of TEST
when I dereference like this **cpp
, I assume that this pointer *++cpp+3
preserves the state after the --
is applied, but cannot imagine yet how it works.
Why the following works the way it works (the cpp[-2]
part):
printf("%p\n", &*cpp[1]); // 0x601060 -> c
printf("%p\n", &*(cpp[0])); // 0x601068 -> c+1
printf("%p\n", &*(cpp[-1])); // 0x601070 -> c+2
printf("%p", &*(cpp[-2])); // 0x601078 -> c+3
It seems that it has reverse order, I can accept &*(cpp[0])
pointing to c+1
, but I would expect &*cpp[1]
to point to c+2
, and &*(cpp[-1])
to c
. Which I found in this question: Are negative array indexes allowed in C?
Let me clarify first the negative index confusion, since we will use it later to answer the other questions:
when I say
*(cpp[-2])
I getQUIZ
, but I would rather expect to get some garbage value.
Negative values are fine. Note the following:
By definition, the subscript operator
E1[E2]
is exactly identical to*((E1)+(E2))
.
Knowing that, since cpp == cp+2
, then:
cpp[-2] == *(cpp-2) == *(cp+2-2) == *cp == c+3
And therefore:
*cpp[-2]+3 == *(c+3)+3 == c[3]+3
Which means the address of "QUIZ"
plus 3 positions of a char
pointer, so you are passing to printf
the address of the character Z
in "QUIZ"
, which means it will start printing the string from there.
Actually, in case you wonder, -2[cpp]
is also equivalent and valid.
Now, the questions:
- How the magic with
*--*++cpp+3
works, I mean what is modified by the -- part that allows me to getMCQ
instead ofTEST
when I dereference like this**cpp
, I assume that this pointer*++cpp+3
preserves the state after the -- is applied, but cannot imagine yet how it works.
Let's break it down (recall that cpp == cp+1
here, as you correctly point out):
++cpp // cpp+1 == cp+2 (and saving this new value in cpp)
*++cpp // *(cp+2) == cp[2]
--*++cpp // cp[2]-1 == c (and saving this new value in cp[2])
*--*++cpp // *c
*--*++cpp+3 // *c+3
And that points to sQuiz
as you correctly pointed out. However, cpp
and cp[2]
were modified, so you now have:
cp[] == {c+3, c+2, c, c}
cpp == cp+2
The fact that cp[2]
changed is not used in the rest of the question, but it is important to note -- specially since you printed the values of the pointers. See:
Why the following works the way it works (the
cpp[-2]
part):printf("%p\n", &*cpp[1]); // 0x601060 -> c printf("%p\n", &*(cpp[0])); // 0x601068 -> c+1 printf("%p\n", &*(cpp[-1])); // 0x601070 -> c+2 printf("%p", &*(cpp[-2])); // 0x601078 -> c+3
First, let's simplify &*x
into x
. Then, doing something similar as above, if cpp == cp+2
(as above), you can see that:
cpp[ 1] == cp[3] == c
cpp[ 0] == cp[2] == c // Note this is different to what you had
cpp[-1] == cp[1] == c+2
cpp[-2] == cp[0] == c+3
- I obviously confuse many things, and may call something a pointer that in reality is not one, I would like to grasp the concept of pointer, so will be glad if someone show me where I am wrong.
You actually got it quite well! :-)
Basically, a pointer is an integer that represents a memory address. However, when you perform arithmetic on it, it takes into account the size of the type it points to. That is the reason why, if c == 0x601060
and sizeof(char*) == 8
, then:
c+1 == 0x601060 + 1*sizeof(char*) == 0x601068 // Instead of 0x601061
c+2 == 0x601060 + 2*sizeof(char*) == 0x601070 // Instead of 0x601062
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With