Jul 9th, 2021 - written by Kimserey with .
Pointers are variables containing addresses of other variables. In C, pointers are used in many scenarios where they improve the code readability or where they are just necessary. In this post, we’ll explore what C pointers represent, how they can be used in functions, and explore the relationship between pointers and arrays.
A pointer contains an address to another variable. For example:
1
2
int a = 1;
int *p = &a;
The *
(star) is called the indirection operator. The &
(ampersend) is used to get the address of a variable. So in this example, we create an integer variable a
, an integer pointer p
and we set the address of a
to the value of the pointer p
.
int *p
means that the value p
points to is of type int
.
If we were to write the assignment in two separate lines, it would be as such:
1
2
int *p;
p = &a;
If we then print the address, value and indirection:
1
2
printf("a:\naddress: %p\nvalue: %d\n\n", &a, a);
printf("p:\naddress: %p\nvalue: %p\nindrection: %d\n\n", &p, p, *p);
We would get the following:
1
2
3
4
5
6
7
8
a:
address: 0x7fff715e831c
value: 1
p:
address: 0x7fff715e8320
value: 0x7fff715e831c
indrection: 1
As we can see, the value of p
is the address of a
, and the indirection *p
is the value of a
. And we can see that p
on itself has its own address.
Because p
points to a
, when we modify *p
, it will modify a
:
1
2
3
4
5
int a = 1;
int *p = &a;
*p = 2;
printf("a: %d\n", a);
printf("p: %d\n", *p);
*p = 2
will assign 2 to a
:
1
2
a: 2
p: 2
and because *p
is of type int
, we can use it in any operation involving integers.
Assigning p
to another pointer would result in the other pointer pointing to the same address:
1
int *p2 = p;
p
and p2
would now point to a
. Since p
is a variable on its own with an address, we can also have a pointer to pointer:
1
int **p2 = &p;
where we assign &p
the address of p
, this creates a double indirection, where **p2
will be the value at the address p
points to.
C passes arguments to functions by value - meaning it makes a copy of the argument making it impossible to change the original value of a variable being passed as argument. If we want to create a function which would modify the arguments from within the function, we can use pointers:
1
2
3
4
5
6
7
8
void swap(int *px, int *py)
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
In our function, the parameters int *px
and int *py
are pointers. Because they hold addresses of the values, we are able to reassign the values. For example:
1
2
int a = 10, b = 20;
swap(&a, &b);
Here we passed directly the addresses of a
and b
just like how we instantiate pointers and we will end up with a = 20
and b = 10
. In the same way, we can pass pointers directly to swap
:
1
2
3
int a = 10, b = 20;
int *p1 = &a, *p2 = &b;
swap(p1, p2);
By passing pointers, we pass the value of the pointers since their values are the addresses of a
and b
. In this case, p1
isn’t px
, p1
is still pass as a copy, but because it copies the address to a
, we end up having what we need. If we check the address of p1
and the address of px
, we will see that they are different:
1
2
3
4
5
&p1: 0x7fffffffda18
p1: 0x7fffffffda10
&px: 0x7fffffffd9e8
px: 0x7fffffffda10
We see that the value of the pointer is copied but the variable itself is a new variable.
Elements in array are stored in a contiguous blocks where each value can be accessed by index a[0], a[1], ..., a[9]
. In the same way as indexes, pointers can be used to navigate through elements of an array and assigning an array to a pointer result in assigning the address of the first value of the array to the pointer.
1
2
int a[10];
int *pa = a;
This is equivalent to:
1
int *pa = &a[0];
By applying the indirection, we can see the content of the first cell of the array:
1
2
a[0] = 20;
printf("%d\n", *pa);
Because array variables are stored in contiguous blocks, and an array assigned to a pointer points to the first element, we can increment the value of the pointer which would increment the address value stored hence point to the next value of the array:
1
2
a[1] = 21;
printf("%d\n", *++pa)
Mutable strings in C are held in character arrays. There is no string
primitive type. For example:
1
char message[] = "hello world!";
Then if we want to pass the string to a function, we can use a pointer:
1
2
3
4
5
6
7
int strlen(char *str)
{
int n;
for (n = 0; *str != '\0'; str++)
n++;
return n;
}
and we can call strlen
just like with any pointers strlen(message)
or strlen(&message[0])
. There is an important difference between declaring an array of character, versus a pointer:
1
2
char message[] = "hello world!";
char *str = "hello world!";
The first declaration is a mutable string, where each separate character can be updated while the second declaration is defining a pointer to a literal string which is immutable. Trying to change any character of the second string result in an undefined behaviour.
1
2
message[0] = 'x';
printf("%s", message);
will print xello world!
, while:
1
*str = 'x';
will result in a segmentation fault.
And that concludes today’s post!
In today’s post, we looked at what pointers were in C programming language. We started by looking at how they could be declared, then we moved on to see how they could be used as parameter of functions and we concluded the post by looking at the link between arrays and pointers. I hope you liked this post and I see you on the next one!