Computational Physics
Allocating space
Comments and questions to John Rowe.
malloc
You have already met the
malloc function:
#include <stdio.h>
#include <stdlib.h>
#define N 100
int
main() {
float *p;
p = malloc(N * sizeof *p);
if (p == NULL) {
fprintf(stderr, "Out of memory!\n");
exit(-1);
}
p[0] = p[1] = 3.14159;
/* Use p for something */
return 0;
}
p above is called a
pointer, a concept
introduced in the previous module.
Once you have allocated some memory for it you can
use a pointer just like an array.
The sizeof operator
For any pointer
p the expression
sizeof *p returns the number of bytes
needed for whatever
p points to. It's the ideal argument to
malloc! There's also a version
sizeof(float) which
is less useful.
NULL
AS with
fopen,
NULL is used to indicate that
malloc ran out of
memory. You must always check that
malloc did not return
NULL. The
exit() function above does pretty much
what it says. By convention, successful programs exit with
zero.
NULL and
exit()are declared in
<stdio.h> and
<stdlib.h> respectively.
You are welcome to check the result of malloc every time
you call it. You are equally welcome to define a function like:
void *xmalloc(size_t n) {
void *p = malloc(n);
if (p == NULL) {
fprintf(stderr, "Out of memory!\n");
exit(-1);
}
return p;
}
and to call
xmalloc instead of
malloc. I know which
I prefer to do!
Arrays of pointers
These work in the obvious way:
#define N 100
#define M 4
int
main() {
float *p[M];
int i;
for (i = 0; i < M; ++i)
p[i] = xmalloc(N * sizeof *p[i]);
p[0][1] = 3.14159;
/* Use p for something */
return 0;
}
Dynamically allocated '2-D arrays'
Of course, often we won't know how many pointers we will need so we
can dynamically allocate them too.
The following function allocates memory that can then be used like a
two dimensional array (but see
note below).
int **
new2dint(int m, int n) {
int **p;
int i;
p = xmalloc(m * sizeof *p);
for (i = 0; i < m; ++i)
p[i] = xmalloc(n * sizeof *p[i]);
return p;
}
int
main() {
int **p = NULL;
int n;
do {
printf("Size of array (>0)?\n");
scanf("%i", &n);
} while (n <= 0);
p = new2dint(n, n);
p[0][0] = 17;
p[n-1][0] = 13;
p[n-1][n-1] = 10;
return(0);
}
Differences from true multi-dimensional arrays
Executive summary: the things above may look like ordinary
two-dimensional arrays but they're not
At first sight p above looks just like a "real" array,
in that their elements are accessed using the same source-level notation:
int array[M][N];
int **p = NULL;
p = new2dint(M, N);
p[1][7] = 17;
array[1][7] = 17;
However,
p and
array are different things.
p
is a pointer to
M pointers to
N ints. By contrast,
array points to a single block of
M*N ints.
They are dereferenced as follows:
p[i][j] => *( *(p+i) + j)
Double deference:
deference
(p+i), add
j and dereference the result.
array[i][j] => *(array + (N*i + j))
Single deference: add
N*i+j to
array
and dereference that.
This is why for functions which have
arguments which are multidimensional arrays you mut tell
the function the size of the array (which the left-most
size being optional):
int oldstylefun(float array[][N]);
int newstylefun(int n, float array[][n]);
int three_d_fun(int m, int n, float array[][m][n]);
Allocating structures
This works just you would expect:
struct thingy *p;
p = xmalloc(sizeof *p);
In practice however it's a very good idea to define a function whose
job is to allocate the space for a structure and do any initialisation:
#include <stdlib.h>
typedef struct wotsit {
float val;
char *str;
int length;
} Wotsit;
Wotsit *newwotsit(int len) {
Wotsit *w = xmalloc(sizeof *w);
w->length = len;
w->val = 0;
w->str = xmalloc(w->length);
return w;
}
int main() {
Wotsit *william = newwotsit(100);
/* Blah, blah, blah */
}
In practice structures are nearly always
dynamically allocated, seldom statically.
Freeing memory
When you've finished with some memory you should get rid of it so it
can be reused. If you fail to do this
your program will gradually use more and more
memory until the computer runs out.
(When your program finishes it will automatically free
any memory it was still using.)
The absolute rule is that you can't use memory after you've freed it
and you can free it only once. To reuse the above example
we might define a function freewotsit():
void freewotsit(Wotsit *w) {
free(w->str);
free(w);
}
The above version is correct. Had we written:
free(w);
free(w->str);
It would have been very, very wrong.