C programming language is famous for its ability to access memory directly by address. For this C has a special data types named pointers. We already mentioned pointers before, in the article about functions. Next you will learn a bit more about this concept.
You define a pointer variables using prefix "*" in front of variable name. A pointer can be initialized with value NULL. This is a constant that is actually = 0. It represents the zero memory location of a computer that is hardware protected and you can not read value or write value on this location.
#include <stdio.h>
int main () {
int *p = NULL;
printf("The value of p is : %x\n", p);
return 0;
}
Address of a variable is extracted with symbol "&" and it can be assigned to a pointer. This is the most common way to initialize a pointer. Once you know the location of a variable you can read or write its content using the pointer instead of variable name.
Pointer Concept
#include <stdio.h>
int main () {
int d = 17;
int m = 11'
int y = 2020;
int *p = &y; // a pointer to integer
printf("Value of p: %0X\n", p ); //a hexadecimal address
printf("Value of *p: %d\n", *p ); //expected value 2020
return 0;
}
Output:
Value of p: AF04
Value of *p: 2020
Sounds exotic but is not hard at all. There are two ways to use a pointer after it is defined. First way to use it as value (with prefix *). Second is to use it as address (without the prefix). Since a pointer is a number it can be printed and you can perform arithmetic operations with it.
#include <stdio.h>
int main () {
int v = 5; // an integer variable
int *p = &v; // a pointer to integer variable
//initial value
printf("p = %0X\n", p );
p = p + 1; //increment
printf("p = %0X\n", p );
p = p - 1; //decrement
printf("p = %0X\n", p );
return 0;
}
Output:
p = 384042E4
p = 384042E8
p = 384042E4
Take a look at last digit of p. It is 4 then it is 8 then is back 4. That means we have changed the address where pointer p is pointing. We have incremented pointer with one, but we have got 4 numbers up. That is pointer arithmetic is "smart". It will give you next address for "int" type. Since my machine is on 32 bit, int occupy 4 bytes. So the increment it will sow me position for the next memory location after my integer.
There are two specific operators to C:
You can use these two operators to perform increments and decrements of pointers. They will be type dependent for pointers. It will increment/decrements an address with the size of data type. If an address point to 4 bytes, the increment will be size of data type divided by 4.
Arrays are defined in C as read only pointers to first element of array. So we can assign this pointer to another control pointer that is not read only. By using pointer arithmetic we can change control pointer to visit all elements of the array.
#include <stdio.h>
int main () {
//define a vector of 10 elements
int v[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
int i; //control variable
int *p; //temporary pointer
p = v; // same as &v
printf("Address of v: %X\n", v ); // v is a read only pointer
printf("Address of v: %X\n",&v ); // pointing to first element
printf("Content of v: %d\n",*v ); // expect: 10 this is first element
/* array traversal using pointer arithmetic */
for ( i = 0; i < 10; i++, p++) {
printf("v[%d] = %d\n", i, *p);
}
return 0;
}
Since the pointers are unsigned integer numbers, you can compare two pointers using relation operators. For array traversal you can detect if one element is precedent, current or next using < , = , > like in example below.
#include <stdio.h>
int main () {
//define a vector of 10 elements
int v[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
//temporary pointer
int *p;
/* array traversal using pointer arithmetic */
for ( p = &v[0]; p <= &v[9]; p++) {
printf("%d,", *p);
}
return 0;
}
This is a strange thing. You can have a collection of pointers. These pointers can point to first element into another collection. The most common array of pointers is *char[]. It represents actually and array of strings, like in the following example:
#include <stdio.h>
int main () {
char *capitals[] = {
"Washington",
"London",
"Bucharest",
"Moscow",
"New Delhi"
};
int i = 0;
for ( i = 0; i < 5; i++) {
printf("capitals[%d] = %s\n", i, capitals[i] );
}
return 0;
}
Now you know almost everything about pointers. So we can continue with some juicy stuff:
You can use a pointer as a result of a function. Without pointers you can not return more than one single result, but using a pointer you can return a structure or array. There is a catch, the pointer or the structure must be allocated on the heap. A local array that is not dynamic allocated is on the stack and can not be returned as a result.
Rules:
#include <stdio.h>
#include <stdlib.h>
int *vector(int n)
{
/* allocate memory on the heap */
int *a = calloc(n, sizeof(int));
if ( a != NULL ) {
for( size_t i=0; i<n; i++ ) {
a[i] = i + 1;
}
}
return a;
}
int main () {
int *v, i, x = 10;
/* generate a vector with x number of elements */
v = vector(x);
/* array traversal using pointer arithmetic */
for (i = 0; i < x; i++) {
printf("%d,", *v);
v++;
}
return 0;
}
Pointers can be used to refer to a struct by its address. This is particularly useful for passing structs to a function by reference or to refer to another instance of the struct type as a field.
The pointer value can be refereed just like any other pointer, using star operator: "*". There is also a specific operator "->" which is referring to the value of a member of the struct.
#include <stdio.h>
/* declare a struct named point */
struct point {
int x;
int y;
};
int main() {
struct point test = { 3, 7 }; //instance of point
struct point *p = &test; //reference to struct instance
// Using "*" to de-reference struct members
printf("p.x = %d\n", (*p).x );
printf("p.y = %d\n", (*p).y );
(*p).x = 2; // alter first member of the struct
(*p).y = 4; // alter second member of the struct
// Using "->" to de-reference struct members
printf("p.x = %d\n", p->x);
printf("p.y = %d\n", p->y);
}
Read next: Functions