Function with a pointer in its declaration. Why? - c

Not sure if the title explains it. What is the difference between:
char *s_gets(char * st, int n);
and
char s_gets(char * st, int n);
I would like to ask also why is there a pointer in the function declaration in the first place? What is its purpose?

char s_gets(char * st, int n); returns a character, that's all.
char *s_gets(char * st, int n); returns a pointer to a character, which can be interpreted as a pointer to a string, if it is the programmer's will.

char *s_gets(char * st, int n);
returns a pointer to char, while
char s_gets(char * st, int n);
returns a plain char.
I would like to ask also why is there a pointer in the function declaration in the first place? What is its purpose?
It indicates that the function returns a pointer value.
C declaration syntax uses something called a declarator to convey the pointer-ness, array-ness, function-ness, or combination thereof of an item. For example, take the declaration
int *p;
The type of p is int * (pointer to int). The type is specified by the combination of the type specifier int and the declarator *p. The int-ness of p is given by the type specifier int and the pointer-ness is given by the declarator *p.
If you substitute p with f(void), you get
int *f(void);
The type of f is "function returning pointer to int". The pointer-ness and function-ness of f are specified with the declarator *f(void).
For any type T and any declarator D, the following are true:
T D; // D is an instance of T
T D[N]; // D is an array of T
T D(); // D is a function returning T
T *D; // D is a *pointer* to T
T *D[N]; // D is an array of pointers to T
T *D(); // D is a function returning a pointer to T
T (*D)[N]; // D is a pointer to an array of T
T (*D)(); // D is a pointer to a function returning T
In both declarations and expressions, the postfix [] subscript and () function-call operators have higher precedence than unary *, so *a[N] will always be parsed as *(a[N]) (array of pointers). To declare something as a pointer to an array or a pointer to a function, you need to explicitly group the * operator with the declarator using parentheses.
Declarators can get arbitrarily complex:
T (*D())[N]; // D is a function returning a pointer to an array of T
T (*D[N])(); // D is an array of pointers to functions returning T
T *(*(*D)[N])[M]; // D is a pointer to an array of pointers to an array of pointers to T

Related

What is the difference between int (*p)[10]=s and int (*o)[5]=&s?

On basis of the convention int (*o)[5]=&s; is the right way for a pointer o to point an array having 5 elements.
We can also write this s in this statement
int (*p)[10]=s;
but why preferring
&s at int (*o)[5]=&s;
as both of them return the same output.
#include <stdio.h>
int main()
{
int s[5]={10,1,2,3,4};
int (*p)[10]=s;
printf("%d\n",*p);
printf("%d\n",**p);
printf("%d\n",&s);
printf("\n");
int (*o)[5]=&s;
printf("%d\n",*o);
printf("%d\n",**o);
printf("%d",&s);
return 0;
}
Output of this program is:
-593812272
10
-593812272
-593812272
10
-593812272
This is not valid:
int s[5]={10,1,2,3,4};
int (*p)[10]=s;
Because you're initializing a variable of type int (*)[10] (a pointer to an array of int of size 10) with an expression of type int *. These types are not compatible.
While this is fine:
int (*o)[5]=&s;
Because the type of the initializer matches the type of the variable.
Also, when printing pointer values, you should use the %p format specifier and cast the argument to void *. Mismatching format specifiers with their associated arguments triggers undefined behavior.
This line
int (*p)[10]=s;
is incorrect. The initializer has the type int * due to the implicit conversion of the array designator s to a pointer to its first element. And the two pointers in the left hand side and in the right hand are not compatible. So the compiler should issue a message.
This line
int (*o)[5]=&s;
is correct. The initializer has the type int ( * )[5] that is the same type of the initialized pointer o.
Pay attention to that to output a value of a pointer you have to use the conversion specifier %p. Otherwise using the conversion specifier %d to output a pointer invokes undefined behavior.
So for example instead of these calls
printf("%d\n",*o);
//...
printf("%d",&s);
you have to write
printf("%p\n", ( void *)*o);
//...
printf("%p\n", ( void * )&s);
The expression *o yields value of the array s that is in turn is implicitly converted to a pointer to its first element.
The values of the expression *o and of the expression &s are the same because it is the address of the extent of memory occupied by the array. But their types are different. The first expression used as an argument of the call of printf has the type int * while the second expression has the type int ( * )[5].

How can I declare a Pointer to a struct in C?

I have learned that pointers can be declared in 3 different ways:
int* a;
int *b;
int * c;
I prefer:
int* a;
While declaring a pointer to a structure, is it correct to write it like following:
struct Card {
int a;
};
struct Card my_card = { 3, 7 };
struct Card* p = &my_card;
(*p).a = 8;
I am confused because everywhere I have found it is declared as following:
struct Card *p = &my_card;
Thanks in advance.
If T is some type specifier then a pointer to an object of the type T can be declared in any of the following ways
T*p;
T* p;
T *p;
T * p;
For example if T is int * then the pointer declaration can look like
int **p;
int ** p;
int * *p;
int * * p;
The same way you can declare a pointer to a structure
struct Card*p = &my_card;
struct Card* p = &my_card;
struct Card *p = &my_card;
struct Card * p = &my_card;
Pay attention to that you may write
T ( *p );
but you may not write
( T* ) p;
Also there exists another subtlety. If you will write for example
int* p1, p2
then the variable p1 has the type int * while the variable p2 has the type int instead of int *.
But if you will write
typedef int * T;
when in this declaration
T p1, p2;
the both variables have the type int *.
It doesn't matter where you put the spaces. struct Card* p and struct Card *p are equally valid; it's just a matter of style as to which one you prefer.
I agree the second form is more common, but what's most important is that you use one form consistently throughout your code. Your boss / teacher / other developers may also have coding style standards that specify which form to use.
IMO int *a is more meaningful than int* a. In C, *a can be read as 'content of a', so int *a can be read as 'content of a is an integer', the same way int a reads as 'a is an integer'. As the other answers cleared it, the spaces do not really matter here- they are all the same to the compiler.
This way of writing also makes more sense while declaring multiple pointer variables like int *a, *b. It's easy to make a mistake writing int* a, b and expecting both a and b to be int pointers.
As far as the compiler is concerned,
T *a;
T* a;
T*a;
T * a;
all mean exactly the same thing - all are interpreted as T (*a) (a has type "pointer to T"). Whitespace is not significant in this case. With your struct type, struct Card *p and struct Card* p do exactly the same thing, and both are interpreted as struct Card (*p).
Syntactically, the * is always bound to the declarator (more on that below). This means that declarations like
T* a, b;
are interpreted as
T (*a), b;
and only declare a as a pointer to T - b is a regular T. This is one of many reasons I advise against using the T* p style (I will give more reasons below).
In C, declarations have two main parts - a sequence of declaration specifiers (type specifiers, struct, union, and enum specifiers, storage class specifiers, type qualifiers, etc.) followed by a comma-separated list of declarators. In a declaration like
static unsigned long int a[10], *p, f(void);
the declaration specifiers are static unsigned long int and the declarators are a[10], *p, and f(void).
The declarator introduces the name of the thing being declared (a, p, and f) along with information about that thing's array-ness, pointer-ness, and function-ness. The type of each item is fully specified by the combination of the declaration specifiers and the declarator.
The structure of the declarator matches the structure of an expression in the code - if you have an array of pointers to int and you want to access a specific int value, you need to index into the array and dereference the result using the unary * operator:
printf( "%d\n", *ap[i] );
The expression *ap[i] has type int, so the declaration of the ap array is
int *ap[N];
The declarator *ap[N] matches the structure of the expression *ap[i].
In a declaration the * and [] and () operators are only there to indicate type - you're not actually dereferencing or indexing or calling anything. However, they do obey the same precedence rules as they do in expressions. Postfix operators have higher precedence than unary operators, so [] and () "bind" before *:
T *a[N]; // parsed as *(a[N]) -- a is an array of pointers
T *f(void); // parsed as *(f(void)) - f is function returning a pointer
To declare a pointer to an array or function, you must explicitly group the * operator with the array or function expression:
T (*a)[N]; // a is a pointer to an array
T (*f)(void); // f is a pointer to a function
And now for the editorial portion of this answer (which you are free to ignore)...
Over time I've become more militant about discouraging the T* p style of pointer declarations. It's the preferred style among C++ programmers, but the more you think about it the less sense it makes, and in my experience it just causes problems.
Its stated purpose - emphasizing the "pointer-ness" of the variable - is spurious. You can't emphasize the "array-ness" of a variable by declaring it as
T[N] a; // syntax error
or the "function-ness" as
T(void) f; // syntax error
because the operands of the postfix [] and () operators are a and f, not T. Similarly, in a declaration like
T* p; // parsed as T (*p);
the operand of the unary * operator is p, not T. And the * operator is unary (prefix), not postfix, so T* just looks wrong anyway. Because it's unary and because whitespace doesn't matter T* p will work as expected, but you're kind of ignoring the rules of the language when you do it.
If nothing else, it's inconsistent with declaring pointers to arrays or pointers to functions:
T (*ap)[N]; // ap is a pointer to an N-element array of T
T (*fp)(void); // fp is a pointer to a function returning T
and declaring a pointer to an array of pointers like
T* (*ap)[N]; // ap is a pointer to an array of pointers to T
is ugly and just indicates confused thinking, and like I said earlier you can't follow that convention with array or function declarations:
T[N] a; // syntax error
T(void) f; // syntax error
and if you use it you will inevitably screw up and write
T* a, b;
when you meant to write
T *a, *b;
Now, the inevitable response to that objection is to put separate declarations on separate lines:
T* a;
T* b;
and yes, there are plenty of good reasons to have one declaration per line, but working around bad practice isn't one of them. Write those declarations as
T *a;
T *b;
instead.

Why does the type cast of malloc in C have the * symbol on the right and not on the left?

In C, a void pointer is declared, usually, like this:
int a = 10;
char b = 'x';
void *p = &a; // void pointer holds address of int 'a'
p = &b; // void pointer holds address of char 'b'
However, the syntax of malloc in C is like this:
ptr = (cast-type*) malloc(byte-size)
What I can't understand is why the "*" symbol in the "cast-type" section of the code above is on the right. I would assume the correct way of writing the same line of code would be like this:
ptr = (*cast-type) malloc(byte-size)
That's because when declaring a pointer the "*" is on the left, not on the right.
What's the problem with my reasoning?
That's because when declaring a pointer the "*" is on the left, not on the right.
The * is on the right of the type name. In void *p = &a; type is void *, a void pointer. It is on the left of the thing it's being applied to, the variable p.
In ptr = (cast-type*) malloc(byte-size) the type is cast-type *, the * is to the right of the type name. The cast is on the left of the thing being cast, the call to malloc, like an adjective.
[I do find it odd that we write type *variable, which makes it seem like the * is part of the variable rather than type* variable which puts the * with the type.]
The * indirection operator is a unary operator, meaning its operand is to the right of the operator, as in *p.
C declarations are broken into two major sections - a sequence of declaration specifiers (type specifiers, storage class specifiers, type qualifiers, etc.) followed by a comma-separated list of declarators. A declarator introduces the name of the thing being declared, along with information about that thing’s array-ness, pointer-ness, and function-ness. In the declaration statement
int a[10], *p, f(void);
the declaration specifier is int and the declarators are a[10], *p, and f(void). Note that the * is bound to the declarator, not the type specifier. Because it’s a unary operator and token on its own, you don’t need whitespace to separate the type specifier from the identifier - all of
int *p;
int* p;
int*p;
int * p;
are equally valid, and all are interpreted as int (*p); - the operand of * is always p, not int or any other type specifier1.
The type of a is "10-element array of int", which is fully specified by the combination of the int type specifier and by the [10] in the declarator. Similarly the type of p is "pointer to int", which again is fully specified by the combination of the type specifier and the * operator in the declarator. And the type of f is "function returning int" by the same reasoning.
This is key - there is no type specifier for pointer, array, or function types - they can only be specified by a combination of one or more declaration specifiers and a declarator.
So how do we specify pointer types (or array or function types) in a cast? Basically, we write a declaration composed of declaration specifiers and a declarator, but with no identifier. If we want to cast something as a pointer to int, we write it as a regular pointer declaration with an unnamed variable - (int *).
This is why I’m not fond of the C++ convention of writing pointer declarations as T* p;; it doesn’t follow the syntax, and it’s inconsistent - a declaration like int* a[N]; is schizophrenic. It also introduces confusion when you write something like T* p, q; because it looks like you intend to declare both p and q as pointers, but only p is.

Typedefs for complex data types

I am attempting to understand the underlying mechanics of how C handles complex typedefs, from a syntax perspective.
Consider the following examples below (references included at end of the question).
typedef int (*p1d)[10];
is the proper declaration, i.e. p1d here is a pointer to an array of
10 integers just as it was under the declaration using the Array type.
Note that this is different from
typedef int *p1d[10];
which would make p1d the name of an array of 10 pointers to type int.
So, if I consider operator precedence for both examples (I'll rewrite them):
int *p1d[10]; // Becomes ...
int* p1d[10];
So, reading left-to-right, and using operator precedence, I get: "Pointer to type int, named p1d, of size 10", which is wrong. As for the other/first case:
int (*p1d)[10];
Which I read as "p1d is a pointer, of type int, and is an array of 10 such elements", which is also wrong.
Could someone explain the rules applied for determining these typedefs? I would like to apply them to function pointers as well, and I'm hoping this discussion will also explain the logic behind const casts (ie: pointer to constant data vs const pointer to variable data).
Thank you.
References:
C Tutorial: Pointers to Arrays: http://www.taranets.net/cgi/ts/1.37/ts.ws.pl?w=329;b=285
Operator Precedence: http://www.swansontec.com/sopc.html
One of my professors wrote this little guide to reading these kinds of declarations. Give it a read, it'll be worth your while and hopefully answer any questions.
All credit goes to Rick Ord (http://cseweb.ucsd.edu/~ricko/)
The "right-left" rule is a completely regular rule for deciphering C
declarations. It can also be useful in creating them.
First, symbols. Read
* as "pointer to" - always on the left side
[] as "array of" - always on the right side
() as "function returning" - always on the right side
as you encounter them in the declaration.
STEP 1
------
Find the identifier. This is your starting point. Then say to yourself,
"identifier is." You've started your declaration.
STEP 2
------
Look at the symbols on the right of the identifier. If, say, you find "()"
there, then you know that this is the declaration for a function. So you
would then have "identifier is function returning". Or if you found a
"[]" there, you would say "identifier is array of". Continue right until
you run out of symbols *OR* hit a *right* parenthesis ")". (If you hit a
left parenthesis, that's the beginning of a () symbol, even if there
is stuff in between the parentheses. More on that below.)
STEP 3
------
Look at the symbols to the left of the identifier. If it is not one of our
symbols above (say, something like "int"), just say it. Otherwise, translate
it into English using that table above. Keep going left until you run out of
symbols *OR* hit a *left* parenthesis "(".
Now repeat steps 2 and 3 until you've formed your declaration. Here are some
examples:
int *p[];
1) Find identifier. int *p[];
^
"p is"
2) Move right until out of symbols or right parenthesis hit.
int *p[];
^^
"p is array of"
3) Can't move right anymore (out of symbols), so move left and find:
int *p[];
^
"p is array of pointer to"
4) Keep going left and find:
int *p[];
^^^
"p is array of pointer to int".
(or "p is an array where each element is of type pointer to int")
Another example:
int *(*func())();
1) Find the identifier. int *(*func())();
^^^^
"func is"
2) Move right. int *(*func())();
^^
"func is function returning"
3) Can't move right anymore because of the right parenthesis, so move left.
int *(*func())();
^
"func is function returning pointer to"
4) Can't move left anymore because of the left parenthesis, so keep going
right. int *(*func())();
^^
"func is function returning pointer to function returning"
5) Can't move right anymore because we're out of symbols, so go left.
int *(*func())();
^
"func is function returning pointer to function returning pointer to"
6) And finally, keep going left, because there's nothing left on the right.
int *(*func())();
^^^
"func is function returning pointer to function returning pointer to int".
As you can see, this rule can be quite useful. You can also use it to
sanity check yourself while you are creating declarations, and to give
you a hint about where to put the next symbol and whether parentheses
are required.
Some declarations look much more complicated than they are due to array
sizes and argument lists in prototype form. If you see "[3]", that's
read as "array (size 3) of...". If you see "(char *,int)" that's read
as "function expecting (char *,int) and returning...". Here's a fun
one:
int (*(*fun_one)(char *,double))[9][20];
I won't go through each of the steps to decipher this one.
Ok. It's:
"fun_one is pointer to function expecting (char *,double) and
returning pointer to array (size 9) of array (size 20) of int."
As you can see, it's not as complicated if you get rid of the array sizes
and argument lists:
int (*(*fun_one)())[][];
You can decipher it that way, and then put in the array sizes and argument
lists later.
Some final words:
It is quite possible to make illegal declarations using this rule,
so some knowledge of what's legal in C is necessary. For instance,
if the above had been:
int *((*fun_one)())[][];
it would have been "fun_one is pointer to function returning array of array of
^^^^^^^^^^^^^^^^^^^^^^^^
pointer to int". Since a function cannot return an array, but only a
pointer to an array, that declaration is illegal.
Illegal combinations include:
[]() - cannot have an array of functions
()() - cannot have a function that returns a function
()[] - cannot have a function that returns an array
In all the above cases, you would need a set of parens to bind a *
symbol on the left between these () and [] right-side symbols in order
for the declaration to be legal.
Here are some legal and illegal examples:
int i; an int
int *p; an int pointer (ptr to an int)
int a[]; an array of ints
int f(); a function returning an int
int **pp; a pointer to an int pointer (ptr to a ptr to an int)
int (*pa)[]; a pointer to an array of ints
int (*pf)(); a pointer to a function returning an int
int *ap[]; an array of int pointers (array of ptrs to ints)
int aa[][]; an array of arrays of ints
int af[](); an array of functions returning an int (ILLEGAL)
int *fp(); a function returning an int pointer
int fa()[]; a function returning an array of ints (ILLEGAL)
int ff()(); a function returning a function returning an int
(ILLEGAL)
int ***ppp; a pointer to a pointer to an int pointer
int (**ppa)[]; a pointer to a pointer to an array of ints
int (**ppf)(); a pointer to a pointer to a function returning an int
int *(*pap)[]; a pointer to an array of int pointers
int (*paa)[][]; a pointer to an array of arrays of ints
int (*paf)[](); a pointer to a an array of functions returning an int
(ILLEGAL)
int *(*pfp)(); a pointer to a function returning an int pointer
int (*pfa)()[]; a pointer to a function returning an array of ints
(ILLEGAL)
int (*pff)()(); a pointer to a function returning a function
returning an int (ILLEGAL)
int **app[]; an array of pointers to int pointers
int (*apa[])[]; an array of pointers to arrays of ints
int (*apf[])(); an array of pointers to functions returning an int
int *aap[][]; an array of arrays of int pointers
int aaa[][][]; an array of arrays of arrays of ints
int aaf[][](); an array of arrays of functions returning an int
(ILLEGAL)
int *afp[](); an array of functions returning int pointers (ILLEGAL)
int afa[]()[]; an array of functions returning an array of ints
(ILLEGAL)
int aff[]()(); an array of functions returning functions
returning an int (ILLEGAL)
int **fpp(); a function returning a pointer to an int pointer
int (*fpa())[]; a function returning a pointer to an array of ints
int (*fpf())(); a function returning a pointer to a function
returning an int
int *fap()[]; a function returning an array of int pointers (ILLEGAL)
int faa()[][]; a function returning an array of arrays of ints
(ILLEGAL)
int faf()[](); a function returning an array of functions
returning an int (ILLEGAL)
int *ffp()(); a function returning a function
returning an int pointer (ILLEGAL)
Simplfying KepaniHaole's rules a bit, it boils down to:
Find the left-most identifer
Work your way out, remembering that absent explicit grouping by parentheses, function-call () and [] bind before *.
Apply recursively to any function parameters.
Thus, T *a[] is an array of pointer tp T, T (*a)[] is a pointer to an array of T, T *f() is a function returning a pointer to T, , and T (*f)() is a pointer to a function returning T.
Taking an example that gives a lot of people heartburn, we can look at the prototype for the POSIX signal function:
void (*signal( int sig, void (*func)( int )))( int );
which reads as
signal -- signal
signal( ) -- is a function with
signal( sig ) -- parameter sig
signal( int sig ) -- of type int
signal( int sig, func ) -- and parameter func
signal( int sig, (*func) ) -- of type pointer to
signal( int sig, (*func)( )) -- a function with
signal( int sig, (*func)( int )) -- an int parameter
signal( int sig, void (*func)( int )) -- returning void
(*signal( int sig, void (*func)( int ))) -- returning a pointer to
(*signal( int sig, void (*func)( int )))( ) -- a function with
(*signal( int sig, void (*func)( int )))( int ) -- an int parameter
void (*signal( int sig, void (*func)( int )))( int ) -- returning void

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