Basics of the C programming language (20 Part Series)
constmodifier on a variable or array tells the compiler that the contents will not be changed by the program.
With pointers, there are two considerations when using
- whether the pointer will be changed
- whether the value that the pointer references will be changed Using the const keyword when declaring a pointer indicates that the value pointed to must not be changed.
To define, use the const keyword before the type:
int value = 999; // define the pointer as const const int * pvalue = &value;
The compiler will then check for any statements that attempt to modify the value pointed to by pvalue and flag such statements as an error:
*pvalue = 888; // results in an error
The value can be changed using the
value variable name (because const was not applied to the initial variable declaration), but it cannot be changed using the pointer.
The pointer address can be reassigned as well, just not the value of the pointer's reference:
int value = 888; pvalue = &value; // pvalue now points to a new address in memory, but is allowed because the pointer itself was not used to make the change
There is a way to ensure that the address stored in a pointer cannot be changed, using a different syntax:
int count = 44; // the asterisk in front of the const keyword ensures that the address cannot be changed int * const pcount = &count;
The above ensures that the pointer will always point to the same thing. The compiler will check that you do not inadvertently try to change the pointer's reference elsewhere in the code:
int item = 34; // the below results in an error for trying to change the address of the pointer pcount = &item; // to recap: //the * placed before the pointer name means the value it points to cannot change using the pointer const int *pointerName ...; // the * placed before the const keyword means the address cannot change int *const pointerName ...;
It is also possible to create a pointer where both the value and the reference are constant in the same declaration...
int item = 25; const int *const pitem = &item;
...or, if the varible the pointer references is declared as a constant as well, then it will be completely immutable: the compiler will not allow changes to variable itself, the pointer address, or the pointer reference value.
const item = 25; const int *const pitem = &item;
The type name
void means "absence of any type."
A pointer of type
void* can contain the address of a data item of any type, and is often used as a parameter type or return value type with functions that deal with data in a type-independent way.
- Any kind of pointer can be passed around as a value of type
- the void pointer does not know what type of object it is pointing to, so it cannot be dereferenced directly -- it must first be explicitly cast into another pointer type before it is dereferenced.
In the example below, the same void pointer is reassigned to 3 different data types and then cast to that data type and dereferenced to print out the value. Note that the syntax for dereferencing below takes the following shape:
- First, an asterisk is used to dereference the pointer itself,
- then, in parentheses with an asterisk, the pointer is cast to a pointer of a specific data type. Another way to think of this is (makeItThisDatatype, makeThis_a_Pointer)
- then the pointer itself is listed
int integerVar = 10; float floatVar = 3.14; char charVar = 'k'; void *voidPointer; voidPointer = &integerVar; printf("value of integerVar is %d \n", *(int *)voidPointer); voidPointer = &floatVar; printf("value of floatVar is %f \n", *(float *)voidPointer); voidPointer = &charVar; printf("value of charVar is %c \n", *(char * )voidPointer);
The above would correctly reassign the pointer andprint out the values of all three datatypes (which would not be possible if the data type had been declared when the pointer was created as, say, and int).
One of the most common uses of pointers in C is as pointers to arrays (including character arrays, i.e. strings). The main reasons for using pointers to arrays are for notational convenience and program efficiency (they allow dynamic memory management). Pointers to arrays generally result in code that uses less memory and executes faster (C was created in the early 1970's when memory and CPU power were precious resources to be used wisely).
For example, if you want to create a pointer to an array of characters (aka a string)
char myString, you can define a pointer called
int *pointerToMyString, which can be used to access the characters in this array.
pointerToMyString to point to the first element in the array, you just declare it as usual:
char myString = "Look! I'm a string!"; // initializing pointers to null is good practice to prevent errors int *pointerToMyString = NULL; pointerToMyString = myString;
Note that the 'address of' operator is not used - the compiler treats assigning a pointer to an array as pointing to the first element's value by default. You can also explicitly declare it as
pointerToMyString = &myString, but it is not necessary.
Pointers can be used to access and iterate through arrays. Pointer arithmetic can also use the increment/decrement operators to 'walk' through them, e.g.
arry; *pArry = arry; //when accessing the array arry[i]; //and *(pArry+i); //are equivalent // however, the below only works on a pointer variable pArry++;
So, you can use the address of a pointer to navigate and iterate through an array.