Worice Worice - 1 year ago 132
C Question

Pointers dereference operator - Syntactic rules

I am unsure on the correct use of the star operator. Please consider the following example:

#include<stdio.h>

int main() {
char *w[3]; //Array of pointers
w[0] = "Apple";
w[1] = "Pear";
w[2] = "Peach";

printf("w[0] = %s, w[1] = %s, w[2] = %s\n", w[0], w[1], w[2]);

char **p = &w[0];
char ***q = &p;
printf("&w[0] = %p, *p = %s, p = %p, q = %p, *q = %p, **q = %s\n",
&w[0], *p, p, q, *q, **q);

return 0;
}


My expectations on the use of pointers:

int n = 3;
int *a = &n; -> a = &n -> *a = 3
int **b = &a; -> b = &a -> *b = &n -> **b = 3
int ***c = &b; -> c = &b -> *c = &a -> **c = &n -> ***c = 3


Above,
p
is a pointer to pointer.
p
holds the address of
&w[0]
and
*p
returns the value inside of
&w[0]
. Should not it be done with
**p = "Apple
?
Same way, should not be
***q = "Apple
, instead of
**q
?

I failed to find a resource to make me very clear the correct use of pointers-to-Npointers operators. In this sense, any suggestion would be highly appreciated. I hope I have been able to adequately communicate my question.

Answer Source

A pointer in C is a variable, as any other, which type is an address in memory.

Unfortunately having just an address that points somewhere in the process memory is not enough to correctly interact without the risk of corruption the other entities (generally variables) that lays adjacently to where the pointer points to. For this reason the language allows qualification of the pointer allowing the user to specify to which object (type) it points, and permit to the compiler to select the way it uses to access the memory in compliance with the pointed object.

Said this, and going back to you original question, it should be clear now that a pointer is a variable that can hold the address of any type, basic or derived, existent in C language.

Now we go to the formal part. To interact with pointers we need operators to work with them, basically an operator that resolves to the address of a variable, one that resolves to the value of the variable from the pointer and a declaration operator to "declare" a pointer.

Let start from the dereference operator *. This operator give the value (dereference) of the object from a pointer to it. But it is also the declarator for a pointer because is the most natural visual representation of a pointer. Look the following declaration:

int * p_int;

Reading the declaration following the C style, from left to right, we can say:

p_int is a variable that dreferenced give the value of an int.

It is a pointer.

Now if we have declared a variable p_int to be a pointer to an object of type int when we dereference it the compiler knows that to give back us an int value it must access the memory bytes starting from where the pointer points to and pack a number of bytes, requested for an int on the machine/compiler we are using, together to form an int.

Anyway a pointer, as any other variable, must be initialized or assigned to contain a valid address and then be usable for something. So we have to init/assign a value to our pointer that must be compatible with the type of object to which it points. If we have a variable:

int an_int;

of compatible type it could fit the scope. But a pointer holds the address of object so we cannot assign directly:

p_int = an_int;  //The compiler will trigger an error for incompatible type

to assign it we must get the address in memory of our variable. We need the unary operator & that give back the address of the object to which it is applied to.

p_int = &an_int;  //we assign to p_int the address in memory of an_int

Of course we can access again the value stored at the address pointed to by our pointer by dereferencing it:

int another_int = *p_int;

Before conclusion we must talk of a kind of "special handling" that C language reserves for arrays. In C the name of an array is equated to the address of its first element. This means that the 2 lines following the array declaration are equivalent:

int array_of_int[10];
int *p_int  = array_of_int;
int *p_int1 = &array_of_int[0];

Now we consider your example. The declaration:

char   *w[3];
char  **p = &w[0];
char ***q = &p;

Must be read as "w is an array of 3 pointers to char, p is a pointer to a pointer of int and q is a pointer to a pointer to a pointer of int.".

Lets decode. Each element of array w holds the address of a char, if in memory there is an array of char starting at that address, for the transitive property of what we said before about arrays, we can say that each w element holds the address of the first char of 3 char arrays of unspecified dimensions. Of course each of these arrays hold the 3 words "Apple", "Pear"and "Peach". In the declaration:

char  **p = &w[0];

we have created a variable that holds the address, in memory, where is saved the value of the pointer to char stored in the 0th element of the array w. Consider that w[0] would give the address where the string "Apple" starts, not the address of the 0th element where is saved the address where starts the string "Apple" (repetitive, but necessary to be clear!). So we use the unary operator & to obtain such an address.

The really interesting point is that the 2 asterisks in the declaration are declarative, not operators by themselves. To clarify consider:

char  **p;
p = &w[0];

This is perfectly equivalent to the previous, but in the first line we declare a pointer to pointer to char, in the second we assign it the address of the first element of w.

This should be enough to explain also the other parts of the question.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download