C Pointer Declaration And Dereferencing
Written by Harry Fairhead   
Monday, 20 July 2015

Pointers and pointer declarations in C cause beginners all sorts of strange problems that experienced C programmers find hard to understand. It all possibly comes from one small misunderstanding. 


operators

 

How it starts

When you are first learning C, or C++ for that matter, you generally encounter something like:

int myint;

right at the start of your journey to be a programmer. You think of this as declaring an integer variable myint. 

Soon after this is generalized to:

int myint, myotherint;

which you think of as declaring two integer variables.

This results in the pattern:

type variable,variable, variable, ... ,lastvariable;

being learned. 

A little while later you learn about pointers and the address and dereference operator.

That is:

&myvariable 

is the address of myvariable i.e. it is where myvariable is stored.

Similarly:

*mypointer

is the value stored at the address in mypointer.

That is it is the value in the location mypointer "points" at.

Notice that the value of mypointer is the address, but the value of *mypointer is what mypointer points at. 

However, at about the same time you learn all of this you are also learning that you have to declare a pointer something like:

int* myintpointer;

This declares a pointer to an integer type and there is a tendency to think that int* is a type - a pointer to int. 

As a result the next step is to try something like;

int* myintpointer, mysecondintpointer;

which clearly, by the slightly wrong rules we have inferred, declares two pointers to int.

With this in mind the beginner tries:

mysecondintpointer = &myint;

and discovers an error message saying something like

"cannot convert from type int* to int".

Now this is mysterious because often, but not always, even the error message says that we have a type of int*.

So why is the declaration wrong?

What is going on?

* is always an operator

Beginners are often told to just treat the declaration of a pointer differently from everything else using something like:

"the asterisk binds to the variable not the type"

- and I have no idea what this means.

The simplest way to think about it is not to think in terms of type at all, but consider the dereferencing operator always acting as the dereferencing operator.  

When you write:

*myintpointer

you dereference the pointer and what you have is therefore an int.

For 

*myintpointer

to be an int it is obvious that myintpointer has to be a pointer and it has to be a pointer to an int. 

Simple and perfectly logical. 

Now consider:

int *myintpointer;

This is a declaration that *myintpointer is an int.

As we have just worked out this means that myintpointer has to be a pointer to an int. 

You don't have think about the dereferencing operator in any way that is different in a declaration - the declaration is simply giving the type of the variable after it has been dereferenced. 

Now you can work out what 

int *myvar1, myvar2;

means without any need to invoke special cases. Declarations are still always of the form 

type variable, variable; 

only now you can apply allowed operators to the variables before they are assigned their type.

In other words:

int *myvar1, myvar2;

declares that *myvar1 is an int and myvar2 is an int.

This is what the explanation that "the asterisk binds to the variable" is trying to tell the beginner, but not quite succeeding. 

You can, of course write things like;

int myvar1,*myvar2, myvar3,*myvar4;

and so on and all of the things you are declaring are ints, but two of them are only ints after dereferencing. 

This also brings us to expressions like:

int **myvar;

Now we know it is clear that this is stating that **myvar is an int. So *myvar has to be a pointer to int and myvar has to be a pointer to a pointer to an int.  And, of course, we can now write things like:

int *myvar1, myvar2, **myvar3;

and so on and know that they are all ints but only after the indicated number of dereferencing. 

Const

Pushing on a little, this point of view (pun almost intended) helps with the dreaded const qualifier. 

What does:

const int myvar1=1:

mean?

Easy, it is a declaration of a constant int and if you try to assign to it after the initial assignment in the declaration then your program will not compile. That is

myvar=2;

will generate a compile time error.

Now what is:

const int *myvar2;

As before, you are declaring that *myvar2 is a constant int.

So myvar2 has to be a pointer to a constant int.

That is, the int is constant but the pointer isn't. The pointer can change and point to something else but the thing it points at can't be changed via the pointer.

This means that: 

const int myint1 = 1;
const int *myvar2;
myvar2 = &myint1;

is fine, but 

*myvar2 = 3;

isn't.  

There is a subtlety here that is worth making clear. The pointer to a constant int can be set to point at a non-constant int. For example:

const int *myvar2;
int myvar3=2;
myvar2 = &myvar3;
myvar3=3;

is perfectly fine.

The fact that the pointer is to a constant int simply stops you from writing:

*myvar2=somthing;

All declaring a pointer to a constant type does is to stop you changing something via a pointer dereference - there may be other legal ways to change the something. 

It has to be admitted that different compilers react differently to any attempt to change a constant variable so you need to be careful. 

Also, notice that you can write:

const int *myvar;

or

int const *myvar;

and they mean the same thing - that *myvar is a constant integer.

However, if you move the dereferencing operator you get a very different result. 

What does:

int *const myvar;

mean?

Here things are almost as logical and we read this as myvar is a constant and *myvar is an int. Notice that the pointer now cannot change, but the value of the int it points at can.

That is:

int myvar2 = 2;
int *const myvar1=&myvar2;
*myvar1 = 3;

is fine because the value 3 is stored in myvar2, which isn't constant, and myvar1 points at myvar2 i.e. is constant throughout the program.

However

myvar1 = &myvar3;

is not allowed. 

Finally, if you can stand it, what does:

int myvar2 = 2;
const int  *const myvar1=&myvar2;

mean?

The answer is that this is constant pointer to a constant integer. That is, *myvar1 is a constant int and myvar1 is a constant pointer.

You can't do

myvar1=&myvar3;

as that would change the pointer; and you can't do:

*myvar1=48;

because that would change the int it is pointing at. 

Once again, it is worth pointing out that you can still modify values if you use the variable directly rather than the pointer.

So while

*myvar1=48;

is illegal 

myvar2=48;

is legal because myvar2 isn't a constant int. 

In other words if you have a pointer then either the thing the pointer points at can be constant, the pointer can be constant or both the pointer and the thing it points at can be constant. 

To summarise:

  1. Declarations are always of the form
    type variable1, variable2, variable3...
    e.g. int var1,var2,var3 are all integer variables.

  2. You can use the dereferencing operating in a declaration
    type variable1,*variable2 ...
    and variable1 and *variable2 are type which means that variable2 must be a pointer to type. 
    e.g. int var1,*var2,var3 means that var1, *var2 and var3 are all integers so var2 has to be a pointer to int. 

  3. const type variable1=value;
    declares variable1 to be a constant type.
    e.g. const int var1=48  declares var1 to be a constant integer and you can't assign to it. 

  4. const type *variable1;
    declares *variable1 to be a constant type - hence variable1 is a non-constant pointer to constant type.
    e.g. const int *var1 is a pointer to a constant integer and you can't use assignments like *var1=0 but you can assign to var1 as in var1=&var2;

  5. type *const variable1;
    declares variable1 to be a constant and *variable1 is a non-constant type. 
    e.g. int *const var1 is a constant pointer to a non-constant integer and you can use assignments like *var1=1 but you can't assign to var1 as in var1=&var2.

  6. const type *const variable1;
    declares variable1 to be a constant and *variable1 to be a constant type. Putting this another way variable1 is a constant pointer to a constant value. 
    e.g. const int *const var1=&var2 is a constant pointer to a constant int and you can assign to either *var1 or var1. 

  7. Finally all of the constant constraints only apply via access using the pointers that you have declared. You can still change values using direct access via any variables you might have declared.

 

operators

 

Related Articles

Power of Operators       

Binary Arithmetic       

Binary - Negative Numbers

Assemblers and assembly language        

Floating point numbers 

Boolean Logic

Computer Memory and Pigeonholes           

The Mod function    

How Memory Works

Inside the Computer - Addressing

Where did the logic go?     

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, FacebookGoogle+ or Linkedin,  or sign up for our weekly newsletter.

 

Banner


Random Gifts For Programmers
24/11/2024

Not really random. Not even pseudo random, more stuff that caught my attention and that I, for one, would like to be given. And, yes, if I'm not given them, I'd probably buy some for myself.



pg_parquet - Postgres To Parquet Interoperability
28/11/2024

pg_parquet is a new extension by Crunchy Data that allows a PostgreSQL instance to work with Parquet files. With pg_duckdb, pg_analytics and pg_mooncake all of which can access Parquet files, is  [ ... ]


More News

 

espbook

 

Comments




or email your comment to: comments@i-programmer.info

Last Updated ( Wednesday, 15 June 2016 )