I want to understand why we need `int **z`? - c

int x=10;
int *y=&x;
int *z= &y;
printf("%d\n",*(*z));
I want to understand why we need int **z? What is wrong here?

Here's a handy table showing the types of various expressions based on your declarations:
Expression Type Value
---------- ---- -----
x int 10
&x int * address of x
y int * address of x
&y int ** address of y
Since the type of the expression &y is int **, you need to declare z as int ** to hold that value (and thus the expression &z would have type int ***).
So, why does it matter? A pointer is a pointer is a pointer, right?
Well, not necessarily. Pointers to different types may have different sizes and representations. From the online 2011 standard:
6.2.5 Types
...
28 A pointer to void shall have the same representation and alignment requirements as a
pointer to a character type.48) Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and alignment requirements. All
pointers to structure types shall have the same representation and alignment requirements
as each other. All pointers to union types shall have the same representation and
alignment requirements as each other. Pointers to other types need not have the same
representation or alignment requirements.
48) The same representation and alignment requirements are meant to imply interchangeability as
arguments to functions, return values from functions, and members of unions.
In the desktop and server world, most architectures are such that all pointers types have the same size and representation, but there are plenty of oddball architectures where that isn't true. You can have a word-addressed architecture where multiple char values are packed into a single word; a char * would need to have a couple of extra bits to index into that word to get to a specific char value. You can have a Harvard architecture where you have fewer address lines for data than you do for code (or vice versa), so pointers to object types and pointers to function types will have different sizes.
There's another issue at play here: pointer arithmetic. Since y points to an object of type int, the expression y++ will advance y to point to the next object of type int. Since z points to an object of type int *, the expression z++ will advance z to point to the next object of type int *. If int and int * have different sizes, then y and z will be advanced by different amounts.
In short, type matters, even for pointers.

Let's take it a step at a time, shall we?
int x = 10;
This code creates an int variable at some memory location and sets its value to 10. This variable can be accessed by the convenient name x.
int *y=&x;
This code creates a pointer to an int variable at some memory location and sets its value tot he address of the x variable. This variable can be accessed by the convenient name y. If you printed the value or y you would get the address of x. If you dereferenced y (by doing *y) and printed the result then you would get 10
int *z= &y; // error
This won't compile: z is a of type pointer to int. Since y is already a pointer to int, when you take its address (by doing &y) what you get back is a pointer to pointer to int. That's why you need the double star.
int **z = &y;
Now this will compile. This code creates a pointer to a pointer to int variable and sets it value to the address of the variable y. If you printed the value z now, you would get get the address to y. If you dereferenced z (by doing *z) and printed the result you would get the address of x.

x is a variable of type int, meaning it holds an integer value. The type of 10 is int.
y is a variable of type int*, meaning it holds a value representing a memory address at which a value of type int is stored. The unary operator & produces the memory address of its argument, and generally the type information is included along with that, so as x is an int the type of the address of whatever is held by x will be int*.
z is a variable of type int**, meaning it holds a value representing the address of a variable of type int*. &y produces the memory address of y, and thus the result is of type int**.
The reason why the type information is included is not just a matter of strict/static typing; the way pointer arithmetic is performed (e.g. p++, equivalent to p += 1) will differ depending on the size of the type being pointed to. So for a pointer to a 4-byte value, incrementing the pointer by 1 will result in a pointer to the location four bytes after the original, while for a pointer to a 1-byte value, incrementing the pointer by 1 will result in a pointer to the location one byte after the original. Essentially, adding an integer to a pointer results in pointer_value + integer*sizeof(underlying_type).
You can, of course, use inline casts on the pointer type to alter the factor on the integer, but that's a topic for another time.

You need a double star because of this.
x = 10 (the memory at the address of x holds hte value 10).
y = the address of x. If you dereferenced y (*y), it would show you 10. If you printed the value at the address of y, it would show you the address of x. If you print the address of y, it will show you something different from these 3 things.
z = to address of y. Remember, when you dereference z now (*z), it is going to give you the value held at y, which is the address of x. When you double dereference this, it gives you the value at address of x, which is what you want (hence, why you need the double dereference).

In c/c++, it's helpful to read variable declarations from right to left in order to get a better understanding of what their types are. In your code segment, this would produce the following description:
x is an int
y is a pointer to an int
z is a pointer to an int
In the first line, you're assigning 10 to x. Since 10 is an int, this succeeds without problems.
In the second line, you're assigning the address of x to y. Since pointers hold addresses, x is an int and y is a pointer to an int, this line succeeds as well.
The third line is where you hit your snag. You're assigning the address of y to z. z is expecting an address to an int by it's getting the address of a pointer to an int.
As DardDust mentioned, you need to change your definition of z so that it's a pointer to a pointer to an integer.
int **z = &y;
Once you've got that, it will be easier to understand the last line and your first question.
the printf function is expecting an int but you're using z. In order to get the int value, you need to do a double dereference. The first dereference will return a pointer to an int, the second the int itself.

x is a variable of type int which is stored somewhere in memory.
&x returns the address of the memory location where x is stored. That's a pointer. Thus the type returned by &x is int *. Now, this pointer is again stored somewhere in memory, using variable y.
And then you want to get the address of z via &y. The variable y already stores a pointer, so now you have a pointer to a pointer to an int, that is a int **.
In the printf statement, you then need to crawl that chain back:
The inner *z reads the memory pointed to by z. Since z has the memory location of y, *z return the memory location of y.
And then you read the memory pointed to by y (indirectly). And y points to x, so you end up reading x.
A bit mind twisting, but vital to understand when working with C.

For every time you need a pointer to you need to add a * to your type:
int x=10;
declares an int and assigns to it the value 10
-> no a pointer to so no *
int *y=&x;
declares a pointer to an int and saves and assigns to it the address of where x is stored
-> 1 a pointer to so 1 *
and finally
int **z= &y;
declares a pointer to a pointer to an int and assigns of it the address where y is stored
-> 2 a pointer to so 2 *

Related

a = &b vs *a = &b — pointer assignment

I have a pointer and a variable:
int *a;
int b;
Is there any difference between assignments
a = &b;
and
*a = &b;
and what are they called (like pointer declaration or something) ?
Types matter.
In case of a=&b, the assignment is valid. You're assigning the address of an integer (type: int *), to another variable of type int *, so this is legit.
In case of *a=&b, this is a constraint violation (for assignement operator, see chapter §6.5.16.1/p1, Constraints, for Simple assignment) and thus not a valid C syntax, thus not required to be compiled by any conforming compiler. To make it a valid C syntax, we need to enforce a typecast, something like
*a= (int) &b;
would make it a syntactically valid C statement which meets the required constraint.
Even, after that, the result is implementation defined.#note Here, you're basically trying to assign the the address of an integer (type: int *) to another variable of type int (*a is of type int). Conversion from a pointer to integer is implementation defined behaviour.
Quoting C11, chapter §6.3.2.3, Pointers
Any pointer type may be converted to an integer type. Except as previously specified, the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined. [....]
[....] And what are they called?
They both are assignment statements.
Note:
Considering a points to a valid memory location already. Otherewise, dererefencing an invalid pointer invokes undefined behavior on it's own.
Pay attention to types on the left and on the right of =.
&b is int *, a is also int * but *a is int.
It's a bit confusing that * has different meanings:
int *a; — here * means that a will be a pointer;
*a = ...; — here * means that we change not address stored in a but value which is located at the address.
So a = &b means "write the address of b to a",
but *a = &b means "write the address of b to *a, i.e. to the address which is stored in a".
Let's suggest that we have this situation:
Address Value
a 0x0001 0x0004
b 0x0002 70
0x0003 80
0x0004 90
At the moment a is 0x0004 and *a is 90.
If you do a = &b, a will be 0x0002 and *a will be 70.
But if you do *a = &b, a will not change, but *a, i.e. value at the address 0x0004, will change to 0x0002.
Given the types, the following assignments would be valid:
a = &b; // int * = int *
*a = b; // int = int
In the second case, a must point to a valid memory location or the behavior is undefined.
*a = &b; // int = int *
is a constraint violation and the compiler will yell at you.
Is there any difference between assignments
a = &b;
and
*a = &b;
Yes. In first case, a and &b (address of b) both are of type int *. They are assignable.
In case of *a = &b, *a is of type int while &b is of type int *. Bothe types are incompatible and type of &b is not converted explicitly to the type of *a. This is a constraint violation. That being said: int types are not capable of holding pointer objects. The only integer types that are capable of holding pointer object are intptr_t and uintptr_t.
7.20.1.4 Integer types capable of holding object pointers
1 The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:
intptr_t
The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:
uintptr_t
These types are optional.
and what are they called (like pointer declaration or something) ?
They are assignment statements.
One notable difference is that second assignment is ill-formed C (because of constraint violation):
*a = &b;
error: assignment makes integer from pointer without a cast
[-Wint-conversion]
C11 §6.5.4/3, Cast operators:
Conversions that involve pointers, other than where permitted by the
constraints of 6.5.16.1, shall be specified by means of an explicit
cast.
The requirement of explicit cast was introduced in C89, in order to to disallow bad practice of implicit conversion between integer and pointer types.
The only exception to this rule is that you can assign pointer value with 0 integer constant, which represents null pointer constant:
a = 0; // more idiomatically: a = NULL;
Like all the other answers have already pointed out, given the variables int *a and int b:
the assignment a = &b is valid (and assigns the address of b to the pointer a, so that *a can be used to access b), whereas
the assignment *a = &b is a constraint violation, since it tries to assign the address of b to the integer pointed to by a, which is not allowed without an explicit cast.
What might be confusing you, however, is that the variable declaration:
int b;
int *a = &b;
is valid, and does exactly the same thing as:
int b;
int *a;
a = &b; // not *a = &b!
That's a very convenient shorthand, since you nearly always want to initialize a variable as soon as you declare it (if only to make sure that you don't accidentally try to use it before it's initialized). But it can be confusing when you first encounter that syntax, since it looks as if you're assigning &b to *a, when it's actually a itself that is getting initialized with the value &b. This is just something that you'll have to learn: variable initialization is not the same as normal assignment, even though it looks confusingly similar.
The first, int a = &b; copies the address of the variable "b" to the
"a".
The second, int *a = &b; copies the address of the variable "b" to the
location "a" points to.
First one is ok but second one invokes UB. ( Unless a is pointing to some valid memory)
Sure there are differents between them
& represent pointer(from pointer you can get value)
* represent value
a=&b (represent a equal point of b)
*a=&b(represent value a equal point of b)
Helped Tutorial
First let me clear you the difference between integer variable and pointer variable:
(1) Integer variable ( e.g: int b, in this case ) is used to store the value of an integer ( of length 4 bytes ). The value of 'b' gets stored in some memory location ( say 0x00000001 ).
(2) Pointer variable (e.g: int * a, in this case) is used to store the memory location of an integer variable. That is, in 'a' we can store the address of an integer variable. The value pointed by a pointer variable can be dereferenced by using ' * ' operator. So 'a' will have the address and ' *a ' will have the value pointed by the value (address) contained in a.
Now answering to your question:
Let's assume that b = 4 and address of b ( &b ) is 0x00000001 ( hexadecimal notation ).
In first type assignment a = &b, the address of variable integer b gets stored in a ( since a is a pointer variable ). Now 'a' has the value 0x00000001 and ' *a ' will have 4.
In second type assignment *a = &b, the address of variable b gets stored in the memory location pointed by a, i.e, in 0x00000001 memory location will have the value 0x00000001 itself. Now 'a' has the value 0x00000001 and ' *a ' will also have the same value 0x00000001.
int *a;
int b;
Is there any difference between assignments `a = &b` and `*a = &b`.
Any variable var of type T has some location in memory, whose adress is either allocated by the compiler or by the static or dynamic linkers. The adress of some variables can be obtained by &var and has the type pointer to T. So, when you apply the & operator you nest the type inside another pointer. a=&b is correct.
On the other hand, *a=&b is not correct. You try to store in the variable *a (that has type int) a pointer to the base address of the variable b (that has type pointer to int). In the architectures where the pointer has 64 bits and int has 32 bits this will lead to failure. On the other hand, on the architectures where pointer and int have the same length, this is possible if you insert a cast. The compiler will not automatically insert a coercion from int* to int.

Addition of a number to a pointer

#include<stdio.h>
int main()
{
float a=10;
float* p=&a;
printf("%u\n",p);
p=p+3;
printf("%u",p);
}
After execution of this program I got 2 memory addresses as an output, thelatter with a value greater by 12 than the former.
#include<stdio.h>
int main()
{
float a=10;
float* p=&a;
printf("%u\n",p);
p=p+3.5;
printf("%u",p);
}
I tried changing 3 to 3.5 but I got an output with equal values of both the addresses. I expected that the value would increment at least by 12 in either cases.
What could be the reason ?
That's how pointer arithmetic works. It's designed to work on arrays.
float array[4];
float *q;
q = array; /* Now q points to the first element of the array: q == &array[0] */
printf("%p\n", q);
q += 3; /* Now q points to the fourth element of the array: q == &array[3] */
printf("%p\n", q);
When you add an integer to a pointer, it points that many elements further into the array. If the size of the array elements is N bytes, then adding x to a pointer adds x*N to the address.
On your machine, it appears that sizeof(float) is 4: you see that x*N=12, with x=3, so N=4.
Note that there are several errors in your code. In your program, p=p+3 has undefined behavior because p points to a single float (which has the same memory layout as an array of 1 float). It is an error to make a pointer point outside the boundaries of an object. On a typical PC compiler you just silently get an invalid pointer; a rare few implementations would detect the invalid pointer as soon as it's computed and abort the program with an error.
Printing the pointer value with %u is also an error. In practice it may work, print garbage, or crash, depending on your compiler and on whether pointers have the same size as unsigned int. Any halfway decent compiler would warn you that printf("%u", p) is incorrect; if yours doesn't, make sure to enable its useful warnings (e.g. gcc -O -Wall if you're using GCC).
There is only three types of pointer arithmetic is allowed in C:
Adding an integer to a pointer.
Subtracting an integer from a pointer.
Subtracting one pointer from another (they should point to same array).
Standard says that:
C11:6.5.6 Additive operators:
2 For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a complete object type and the other shall have integer type. (Incrementing is equivalent to adding 1.)
3 For subtraction, one of the following shall hold:
— both operands have arithmetic type;
— both operands are pointers to qualified or unqualified versions of compatible complete object types; or
— the left operand is a pointer to a complete object type and the right operand has integer type.
Any other arithmetic operation is invalid and will invoke undefined behavior. Note that the correct specifier for printing address is %p.
the program contains several errors and poor programing practices
#include<stdio.h>
int main()
{
float a=10; // init floats with float values, so use '10.0f'
float* p=&a;
printf("%u\n",p); // print addresses with '%p' not '%u'
p=p+3; // now 'p' is pointed to some unknown area
printf("%u",p); // print addresses with '%p' not '%u'
}
good thing the code did not 'de-reference' 'p' after 'p'
was modified, because that would have been undefined behaviour
possibly leading to a seg fault event

Explaining a line of code in C - pointers and cast

We have some float x and we convert it into i, but could anyone please explain what does do in depth first &, then cast and finally *?
int i = *(int*)&x;
&x
Gets a pointer to x
(int*)&x
Casts that pointer to an int*, i.e. a pointer to an int
*(int*)&x
Dereferences the resulting pointer, reading the memory of variable x as if it were an int.
Without knowing the type x is, it's hard to tell what the code's purpose. Most likely, if x is a float, it's being used to get the binary representation of the float (which is impossible to do by just casting to an int, because it does a float to int conversion)
&x: Get a pointer to x
(int*): Cast that pointer to a pointer to an int - this does not actually cast x
*: Retrieve the value the pointer points to
Since a pointer cast doesn't actually perform type conversion, this line of code has the effect of reading the internal bits used to store the float x, and storing those bits int the integer i. Basically, this is a reinterpret_cast. (reinterpret_cast is a C++ feature, but it does exactly the same thing as this C syntax.)
"&" means getting the reference of a variable. So &x gets the reference to a float and has type float*.
(int*) is a cast. Therefore you are casting from float* to int*.
The last "*" is for dereferencing the int*, that is, getting the value of the int*, which is an int.
Lets break it down
&x; //this gets you the address of x
(int *)&x; //this cast the address to be pointing at an integer value
//(before it was pointing at a float value)
*(int *)&x; // now the last * dereferences that value
assuming in your system, long is 8bytes and int is 4bytes. so you get only 4 bytes of the 8 bytes of x. As int is usually 4 bytes and long 8 bytes. Now which of the 4 bytes you get (the first 4 or the last 4) depends on the endianness of your system..
For more info on endianness, read this http://en.wikipedia.org/wiki/Endianness

Why should I use double pointer variable to receive another pointer's address(&ptr1)

int num = 45,*ptr1,*ptr2;
ptr1=&num;
ptr2=&ptr1;
printf("%d\n",*ptr1);
I've been thinking about this question for a while, but couldn't find a way to understand it,why &ptr1 can not be assigned to ptr2 in line 3, &ptr1 is a pointer's address,this address is no different from other address like an address of an integer, say
int a=1;
ptr2=&a;
Which means that I can assign an integer's address to a pointer,but not a pointer's address to a pointer,what differences between these two "address" could possibly make them different? Address of common variables can be assigned to single pointer,but address of pointers can not be assigned to single pointer?
I know the right way to do it is use double pointer to declare ptr2,but why single pointer can't?
Simply put, pointers are not addresses, they are varibles representing an address with a type. So the types have be compatible for pointers to assign (with the exception of void * generic pointer).
ptr2 = &ptr1;
ptr1 has a type of int *, so &ptr1 has a type of int **, it's not the same with ptr2, which has a type of int *.
Reference: C99 6.5.16.1 Simple assignment
both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right.
Yes you can assign a pointer's address to a pointer, but it must be a pointer to a pointer variable.
int **ptr3;
ptr3 = &ptr1;
The reason you can't assign it the way you were trying is that a pointer to an int is not the same as an int. Pointers must be pointing to the same type to be compatible. If you really know what you're doing you can explicitly cast it, but that's a path to danger.
Your code is wrong. This expression:
ptr2 = &ptr1;
Attempts to make an int * out of an int ** without a cast. The C standard forbids such conversions without an explicit cast.
The reason it's not allowed is that pointer types aren't guaranteed by the standard to all be the same size - so the pointer to your pointer might not fit in the variable you declared to be a pointer to an int.
Since pointers to any type can be converted to and from void * implicitly, you could write (correct, but probably confusing) analogous code to that in your question:
int num = 45;
void *ptr1, *ptr2;
ptr1 = &num;
ptr2 = &ptr1;
But doing so will require you to carry around all of the type information in some other way:
printf("%d\n",*(int *)ptr1);
printf("%d\n",*(int **)ptr2);
The short answer is that type matters; a pointer to int is a different, incompatible type from pointer to pointer to int. As others have mentioned, different pointer types may have different sizes and representations.
A pointer value is not just an address; it has additional type semantics. For example, the expression ptr++ will advance the pointer to the address of the next object of the base type. If the base type is char, then the pointer is advanced 1 byte. If the base type is int, the pointer is advanced sizeof (int) bytes.
Simply put because it will confuse the compiler. The compiler can work only according to the language standard. It doesn't have a brain of its own.
The language standard tells the compiler that if there is a int *
go to the address stored in that variable and use it.
In case there is a int ** then it tells it
go to the address in that variable. You aren't done yet as that is also an address. Go there and use what is present there.
This goes on and on for int *** and so on.
Hope this helps you to get over this basic confusion.
If you could assign any address to any pointer regardless of type, on the grounds that one address is just like any other address, consider the trouble you could get yourself into if the following became legal:
int n = 40;
int * p = &n;
int ** pp = &n; /* Typeless address assignment as you would like */
printf("%d\n", **pp); /* Bad Things happen here */
or the other way round:
int n = 40;
int * p = &n;
int * p2 = &p; /* More typeless address assignment */
printf("%d\n", *p2); /* Definitely not what you want */
Even if one address was the same as any other, sensibly dereferencing a pointer would become somewhat troublesome if things worked the way you suggest.
The reason you can't do what you suggest is that the type information you'd lose under your proposal is needed for dereferencing to work. If all you wanted pointers to do was to store and retrieve addresses, you'd have a point, but they're not just used for this. If they were, we could just have void pointers and be done with it.
I completely agree to your statement that when pointer variable always would store a value that is integer, as the address to any variable/array would be an integer.
But still the data-type that is used to declare the pointer is the one of whose address it would be storing.
There are 3 points:
1. The bits that are used while storing integer values differ from machine to machine.
i.e. 32-bit, 64-bit and further more complications may add-up.
2. Memory occupied i.e. bytes of data stored in it. Reason is : somewhere even the pointer variable is stored in memory. Right?
3. There are certain operations associated with pointers like ++ or --.
Remember, pointer type is dependent on the type of variable it points to.
This is the reason/need for the pointer to pointer.

How to understand the pointer star * in C?

I'm struggling with the pointer sign *, I find it very confusing in how it's used in both declarations and expressions.
For example:
int *i; // i is a pointer to an int
But what is the logic behind the syntax? What does the * just before the i mean? Let's take the following example. Please correct me where I'm wrong:
char **s;
char *(*s); // added parentheses to highlight precedence
And this is where I lose track. The *s between the parantheses means: s is a pointer? But a pointer to what? And what does the * outside the parentheses mean: a pointer to what s is pointing?
So the meaning of this is: The pointer pointing to what s is pointing is a pointer to a char?
I'm at a loss. Is the * sign interpreted differently in declarations and expressions? If so, how is it interpreted differently? Where am I going wrong?
Take it this way:
int *i means the value to which i points is an integer.
char **p means that p is a pointer which is itself a pointer to a char.
int i; //i is an int.
int *i; //i is a pointer to an int
int **i;//i is a pointer to a pointer to an int.
Is the * sign interpreted differently in declarations and expressions?
Yes. They're completely different. in a declaration * is used to declare pointers. In an expression unary * is used to dereference a pointer (or as the binary multiplication operator)
Some examples:
int i = 10; //i is an int, it has allocated storage to store an int.
int *k; // k is an uninitialized pointer to an int.
//It does not store an int, but a pointer to one.
k = &i; // make k point to i. We take the address of i and store it in k
int j = *k; //here we dereference the k pointer to get at the int value it points
//to. As it points to i, *k will get the value 10 and store it in j
The rule of declaration in c is, you declare it the way you use it.
char *p means you need *p to get the char,
char **p means you need **p to get the char.
Declarations in C are expression-centric, meaning that the form of the declaration should match the form of the expression in executable code.
For example, suppose we have a pointer to an integer named p. We want to access the integer value pointed to by p, so we dereference the pointer, like so:
x = *p;
The type of the expression *p is int; therefore, the declaration of p takes the form
int *p;
In this declaration, int is the type specifier, and *p is the declarator. The declarator introduces the name of the object being declared (p), along with additional type information not provided by the type specifier. In this case, the additional type information is that p is a pointer type. The declaration can be read as either "p is of type pointer to int" or "p is a pointer to type int". I prefer to use the second form, others prefer the first.
It's an accident of C and C++ syntax that you can write that declaration as either int *p; or int* p;. In both cases, it's parsed as int (*p); -- in other words, the * is always associated with the variable name, not the type specifier.
Now suppose we have an array of pointers to int, and we want to access the value pointed to by the i'th element of the array. We subscript into the array and dereference the result, like so:
x = *ap[i]; // parsed as *(ap[i]), since subscript has higher precedence
// than dereference.
Again, the type of the expression *ap[i] is int, so the declaration of ap is
int *ap[N];
where the declarator *ap[N] signifies that ap is an array of pointers to int.
And just to drive the point home, now suppose we have a pointer to a pointer to int and want to access that value. Again, we deference the pointer, then we dereference that result to get at the integer value:
x = **pp; // *pp deferences pp, then **pp dereferences the result of *pp
Since the type of the expression **pp is int, the declaration is
int **pp;
The declarator **pp indicates that pp is a pointer to another pointer to an int.
Double indirection shows up a lot, typically when you want to modify a pointer value you're passing to a function, such as:
void openAndInit(FILE **p)
{
*p = fopen("AFile.txt", "r");
// do other stuff
}
int main(void)
{
FILE *f = NULL;
...
openAndInit(&f);
...
}
In this case, we want the function to update the value of f; in order to do that, we must pass a pointer to f. Since f is already a pointer type (FILE *), that means we are passing a pointer to a FILE *, hence the declaration of p as FILE **p. Remember that the expression *p in openAndInit refers to the same object that the expression f in main does.
In both declarations and expressions, both [] and () have higher precedence than unary *. For example, *ap[i] is interpreted as *(ap[i]); the expression ap[i] is a pointer type, and the * dereferences that pointer. Thus ap is an array of pointers. If you want to declare a pointer to an array, you must explicitly group the * with the array name, like so:
int (*pa)[N]; // pa is a pointer to an N-element array of int
and when you want to access a value in the array, you must deference pa before applying the subscript:
x = (*pa)[i];
Similarly with functions:
int *f(); // f is a function that returns a pointer to int
...
x = *f(); // we must dereference the result of f() to get the int value
int (*f)(); // f is a pointer to a function that returns an int
...
x = (*f)(); // we must dereference f and execute the result to get the int value
My favorite method to parse complicated declarators is the clockwise-spiral rule.
Basically you start from the identifier and follow a clockwise spiral. See the link to learn exactly how it's used.
Two things the article doesn't mention:
1- You should separate the type specifier (int, char, etc.) from the declarator, parse the declarator and then add the type specifier.
2- If you encounter square brackets which denote an array, make sure you read the following square brackets (if there are any) as well.
int * i means i is a pointer to int (read backwards, read * as pointer).
char **p and char *(*p) both mean a pointer to a pointer to char.
Here's some other examples
int* a[3] // a is an array of 3 pointers to int
int (*a)[3] //a is a pointer to an array of 3 ints
You have the answer in your questions.
Indeed a double star is used to indicate pointer to pointer.
The * in declaration means that the variable is a pointer to some other variable / constant. meaning it can hold the address of variable of the type. for example: char *c; means that c can hold the address to some char, while int *b means b can hold the address of some int, the type of the reference is important, since in pointers arithmetic, pointer + 1 is actually pointer + (1 * sizeof(*pointer)).
The * in expression means "the value stored in the address" so if c is a pointer to some char, then *c is the specific char.
char *(*s); meaning that s is a pointer to a pointer to char, so s doesn't hold the address of a char, but the address of variable that hold the address of a char.
here is a bit of information
variable pointer
declaring &a p
reading/ a *p
processing
Declaring &a means it points to *i. After all it is a pointer to *int. An integer is to point *i. But if consider j = *k is the pointer to the pointer this, means &k will be the value of k and k will have pointer to *int.

Resources