Observera att detta är en arbetsversion av sidan!
Dölj style3dev.css för att bli av med annoteringarna!

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

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 variables
Code 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.
}
Examples of usage:
int main() { int x = 1; int y = 2; swap(&x, &y); printf("x : %d\n", x); // will print 2 printf("y : %d\n", y); // will print 1 }

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:

CodeExplanation
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:

while (*(s++)) { n++; }
or, even more compact:
for( ; *(s++); n++);

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.

CodeComment
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 typeExplanation
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:

int readArray(double a[]) { int i = 0; while (scanf("%lf", &a[i])==1) { i++; } return i; }
This function was relying on the array 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:

double *readArray() { double *a = (double *)malloc(100*sizeof(double)); int i = 0; while (scanf("%lf", &a[i])==1) { i++; } return a; }
There are two problems:
  1. the caller doesn't know the number of values the function has read and
  2. the function can not handle more than 100 numbers.
Since the function actually counts the values (the variable 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:
double *readArray(int *ptr) { ... ... *ptr = i; return a; }
To be able to handle any number of values we can use make use of 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.
Note: It is now the caller's responsibility to free the allocated area!

Exercises:

  1. 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?
  2. 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()); }
    1. Does it work?
    2. What will it print (if it works)?
    3. Can you foresee any problems with it?
    4. Remove the function g and change the return statement in f to  return a + 1;  which should mean the same thing. Run it! If the results differ - explain!

Go to next lesson.

Valid CSS!