Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use * notation to both define a pointer and dereference one?

Tags:

c

notation

Is there a reason the language designers of c used the star for both defining a pointer type

int* x;

and dereferencing a pointer?

int y = *x;

Using the same character for two different things seems confusing, especially since they're always used in the same context.

like image 861
user2977618 Avatar asked Mar 30 '14 04:03

user2977618


1 Answers

There is an excellent answer on this topic on Quora by Brian Bi. I reproduce it here verbatim. http://www.quora.com/C-programming-language/Why-doesnt-C-use-better-notation-for-pointers

The fact that an asterisk is used when declaring a pointer and an asterisk is used when dereferencing a pointer is not some sort of usability glitch in C. It is intentional.

There are two other notations that work like this. You use square brackets when declaring an array, and you use square brackets when accessing an array. You use parentheses when declaring a function pointer, and you use parentheses when calling a function pointer.

Declaration follows use.

Consider the following declaration:

int *a;

This declaration tells you that *a is of type int. In other words, a is a pointer to int.

How about this:

int *a[10];

This declaration tells you that *a[i] (where i is between 0 and 9, inclusive) is of type int. Since array indexing has higher precedence than pointer dereferencing, this tells you that a is something that you can use [] on (*i.e., *an array), then use * on (i.e., a pointer)... and you will get an int as a result. In other words, a is an array[10] of pointers to int.

int (*a)[10];

This declaration tells you that (*a)[i] is of type int. Since a is something you can use * on, it must be a pointer. And then you can use [] on the result to get an int. So we see that the type of a must be pointer to array[10] of int.

Notice that the above two declarations differ only in parentheses! But you don't have to memorize where parentheses should go for every type you might want to declare. Write down how you would use the variable, and slap the final type on at the beginning, just as in the above two examples. Any parentheses you have to insert for precedence reasons, include as well in the declaration.

Many C programmers have trouble remembering how to declare function pointers. This, too, becomes easy when you remember that declaration follows use. For example, let's declare a function pointer that we could use to hold the address of the function strcpy. This variable is something that, in order to use, you first dereference (with *), then call, using (), where the parentheses contain two arguments; the first being char*, and the second const char*. And the result is a char*.

Let's call our function pointer p. The first thing we do is dereference, so we write down *p. Then we call *p with the arguments char* and const char*, so write those down in parentheses next to *p: (*p)(char*, const char*). (The parentheses are required around *p because function calling has higher precedence than dereferencing.) Finally, prepend the result of the usage, that is, the char* the function returns. So our declaration is:

char* (*p)(char*, const char*);

const is a special case. The keyword const in a declaration applies to whatever thing you would have if you only partially use the variable, but stop once you "get" to it. It means that that particular thing can't be changed.

Now, I'm sure you know that const char *s; declares a pointer to constant chars. That makes sense, since the thing at the beginning is const char, i.e., the result of *s is a const char. However, you can also write it in the form char const *s;. The interpretation is that we can "get" as far as *s, and then we see the word const, so it means what we have up to this point is const. Ignoring the const for a moment, we see char. So *s is a char, and it's also const because the word const precedes *s.

Now what about char *const s;? This means const pointer to char. The pointer can't be changed, but the thing it points to can. This is because the keyword const comes directly before s, so it "lies in the way" of the dereferencing operator, so to speak. s is const before it is dereferenced, but *s is not const. The declarations char const *const s; or const char *const s; mean const pointer to const char.

For double (and higher) pointers, it's the same deal. const char **p; declares a pointer to pointer to const char, as does char const **p;. However, char *const *p; declares a pointer to const pointer to char, and char **const p; delcares a const pointer to pointer to char.

Just remember that declaration follows use, and you'll never be confused by declaration syntax again.

like image 65
4 revs, 3 users 71% Avatar answered Nov 08 '22 21:11

4 revs, 3 users 71%