Lesson 3: Pointers and dynamic memory
Purpose: | Learn how to use pointers and memory allocated on the heap |
Contents: |
Pointers, malloc , free .
|
References: |
The tutorialspoint about
There is also relevant material in wikibooks: C Programming. |
Pointers
- A variable has an address in the memory.
-
A variable's address can be obtained using the unary
address operator
&
. We have already used that in calls toscanf
. - An address can, like any other value, be stored in a variable, a so called "pointer variable".
-
A pointer variable is declared using the unary
"dereferencing operator",
*
, in a somewhat peculiar way.Example: The following code declares three pointer variables. Two of them are used to point to variables containing
int
s and one to a variable containing adouble
.int *p, *q; double *r; -
The dereferencing operator
*
can be used to access the value stored at the place the pointer is referring to. -
It is possible to do arithmetic with pointers.
For example, ifp
points to an element in an array withint
s,p + 1
points to the next element in the array. -
There is pointer constant
NULL
(always 0) meaning "pointer to nothing".
Example: A swap function
This function swaps the contents of two variables. Since C uses call by value, we must give the addresses of the variablesCode | Explanation |
---|---|
void swap(int *xp, int *yp) { |
xp and yp are pointing to
the variables whose value should be swapped.
|
int temp = *xp; |
Gets the value stored at the address xp and saves
it in a local variable.
|
*xp = *yp; |
Gets the value stored at yp and puts it in the place
pointed to by xp .
|
*yp = temp; |
Copies the saved value to the place pointed to by yp .
|
} |
Using pointers to array elements
It is possible to use pointer arithmetic to move around in the computer memory which serves as an alternative to indexing in arrays.
Example: The strlen
function can be written as follows:
Code | Explanation |
---|---|
int strlen(char *s) { | The parameter can be declared as a pointer value as well as an array. The function can still be called with a character array as parameter. |
int n = 0; while (*s != '\0') { n++; | |
s++; | Adding 1 to a pointer will cause the pointer to point to the next element. |
} return n } |
The iteration can be written in many ways. The inveterate C programmer would probably write:
Note that everything is done in the "control part" of the for
-statement.
There is nothing left to do in the interior of the loop - that is why there is ;
at the end of that line.
These latter two variations rely on the fact that the null character, '\0'
, has the character code 0,
which is regarded as false while all other values are
regarded as true.
(Remember that strlen
is defined in the string
library
so you don't have to write it at all.)
Note that, although a pointer is just an address to a particular byte in the memory, we tell C what type of data we have stored there. Adding 1 to a character pointer will increase the address with 1 (the next byte), while adding 1 to a double pointer will increase the address with 8 (the next double).
Here follows a bunch of commented statements showing how pointers can be used.
Code | Comment |
---|---|
int x = 1, y = 2; int a[] = {4, 3, 2, 1}; int *ip, *jp; | |
ip = &x; |
ip points to x
|
y = *ip; |
Same as y = x;
|
*ip = 3; |
Same as x = 3;
|
jp = ip; |
Now both ip and jp are pointing to x .
|
*jp = *jp + 5; |
Same as x = x + 5;
|
(*ip)++; |
Same as x++;
|
jp = NULL; |
A null reference. Same as jp = 0;
|
*jp = 2; | Illegal! It is not possible to dereference a NULL pointer. |
ip =&a[0]; |
Points to the first element i a .
|
jp = ip + 1; |
Points to the second element in a .
|
*jp = 0; |
Same as a[1] = 0;
|
*(ip + 3) = *(ip + 1) + 2; |
Same as a[3] = a[1] + 2;
|
ip = a; |
Same as ip = &a[0];
|
Different type of memory
C uses two types of memory: the stack and the heap.
The memory needed by a function (i.e. local variables, arrays, constants, ...) is automatically allocated on the stack when the function is called and automatically
released when the function returns to the caller.
This means, among other things, that local variables can not retain their values.
All memory we have used so far has been allocated on the stack.
Memory on the heap is allocated by explicitly calling an allocation function and is kept allocated until explicitly released by another function.
The most important functions, types and operators for handling dynamic memory are:Function or type | Explanation |
---|---|
void * | An unspecified pointer. Can be type casted to any pointer type. |
void * malloc(int size) |
Allocates a block of size bytes (memory units) and returns a pointer to it.
|
void * realloc(void * ptr, int newSize) | Changes the size of an already allocated block. The block may be moved to another location but the contents will then be copied. |
void * free(void * ptr) |
Deallocates a block of memory earlier allocated by malloc or realloc .
|
sizeof(data type) | Returns the amount of memory units needed to represent the specified type. |
The functions malloc
, calloc
and realloc
are declared in stdlib.h
which thus should be included.
In lesson two there was a function for reading data into an array looking like:
a
provided by the caller being large enough.
A better solution is to let the function create the array and return a pointer to it as the function value. A first (not very good) approximation:
- the caller doesn't know the number of values the function has read and
- the function can not handle more than 100 numbers.
i
), it's
just a matter of communicating that with the caller.
In the version from the previous lesson we returned this as the function value but now
the function value is pointer to the created array.
The solution is then to add a pointer parameter pointing to an int where
the number of read values is to be stored:
realloc
to let the allocated area grow (or shrink) to the correct size:
Code | Comment |
---|---|
double *readArray(int *n) { int size = 10; double *a = (double *)malloc(size * sizeof(double)); | Initially, a block for 10 doubles is allocated. |
int i = 0; while (scanf("%lf", &a[i])==1) { i++; | Read and count. |
if (i==size) { size = 2*size; a = (double *)realloc(a, size * sizeof(double)); } | If the allocated block is full, reallocate a twice as big block |
} a = (double *)realloc(a, i); *n = i; return a; } |
Give back the unused portion of the allocated area, set the number of read values and return a pointer to the block. |
Exercises:
-
Write a function
char *readLine()
that reads a line from standard input, saves it in an dynamically allocated area and returns a pointer to it.
How should end of file be handled? Do we need a parameter for returning the line length? -
Look at the following code:
#import <stdio.h> int *g(int *a) { return a + 1; } int *f() { int a[] = {1, 2, 3, 4, 5}; return g(a); } int main() { printf("%d\n", *f()); }
- Does it work?
- What will it print (if it works)?
- Can you foresee any problems with it?
-
Remove the function
g
and change the return statement inf
toreturn a + 1;
which should mean the same thing. Run it! If the results differ - explain!
Go to next lesson.
Dölj style3dev.css för att bli av med annoteringarna!