Scientific Programming in C IV. Pointers Susi Lehtola 1 November 2012
Pointers The feature at the heart of C are pointers, which are simply pointers to memory addresses. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 2/45
Memory Programs are run in the computer memory. The logical memory seen by the program is composed of physical memory (RAM), and virtual memory (disk swap). Logical memory Physical memory Virtual memory The kernel of the operating system performs the memory management, in which it tries to keep only the active stuff in the (fast) physical memory, and to cache the unneeded stuff onto the (slow) disk swap space. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 3/45
Memory management When a variable is declared, the operating system reserves some memory for its storage. free int k float x double y free In C you have direct access to the memory. You can get the memory address of a variable with the & operator, and deference a memory addess get its contents with the * operator. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 4/45
Example Pointers are declared as double x =1.0; double y=&x ; Now y is a pointer to a double, and it is initialized to the address of x. double x=1.0; double y=&x; other stuff double x 1.0 in memory y Scientific Programming in C, fall 2012 Susi Lehtola Pointers 5/45
Example, cont d #i n c l u de <s t d i o. h> i n t main ( void ) { double x =1.0; / y p o i n t s to the memory a d d r e s s o f x / double y=&x ; / P r i n t i n i t i a l v a l u e o f x / p r i n t f ( x = %f \n, x ) ; } / Set the v a l u e o f the memory s l o t to 2. 0 / y =2.0; p r i n t f ( x = %f \n, x ) ; return 0 ; Scientific Programming in C, fall 2012 Susi Lehtola Pointers 6/45
Example, cont d $. / a. out x = 1.000000 x = 2.000000 Scientific Programming in C, fall 2012 Susi Lehtola Pointers 7/45
Function arguments Function arguments are passed by value in C, in contrast to, e.g., Fortran, where they are passed by reference. #i n c l u de <s t d i o. h> void c h a n g e v a l u e ( i n t n ) { n=3; } i n t main ( void ) { i n t n=1; p r i n t f ( n=%i \n, n ) ; c h a n g e v a l u e ( n ) ; p r i n t f ( n=%i \n, n ) ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 8/45
Function arguments, cont d The result is $. / a. out n=1 n=1 since the n modified in the change value function is just a local copy. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 9/45
Function arguments, cont d If you want the value to be changed, you need to supply a pointer: #i n c l u de <s t d i o. h> void c h a n g e v a l u e ( i n t n ) { n=3; } i n t main ( void ) { i n t n=1; p r i n t f ( n=%i \n, n ) ; c h a n g e v a l u e (&n ) ; p r i n t f ( n=%i \n, n ) ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 10/45
Function arguments, cont d Now the result is the wanted one $. / a. out n=1 n=3 Scientific Programming in C, fall 2012 Susi Lehtola Pointers 11/45
sizeof You can get the size of a datatype with the sizeof function. #i n c l u de <s t d i o. h> i n t main ( void ) { p r i n t f ( s i z e o f ( f l o a t )=%u\n, s i z e o f ( f l o a t ) ) ; p r i n t f ( s i z e o f ( double)=%u\n, s i z e o f ( double ) ) ; p r i n t f ( s i z e o f ( i n t)=%u\n, s i z e o f ( i n t ) ) ; p r i n t f ( s i z e o f ( l o n g i n t)=%u\n, s i z e o f ( long i n t ) ) ; } p r i n t f ( s i z e o f ( f l o a t )=%u\n, s i z e o f ( f l o a t ) ) ; p r i n t f ( s i z e o f ( double )=%u\n, s i z e o f ( double ) ) ; p r i n t f ( s i z e o f ( i n t )=%u\n, s i z e o f ( i n t ) ) ; p r i n t f ( s i z e o f ( l o n g i n t )=%u\n,\ s i z e o f ( long i n t ) ) ; return 0 ; Scientific Programming in C, fall 2012 Susi Lehtola Pointers 12/45
sizeof, cont d On x86 s i z e o f ( f l o a t )=4 s i z e o f ( double )=8 s i z e o f ( i n t )=4 s i z e o f ( l o n g i n t )=4 s i z e o f ( f l o a t )=4 s i z e o f ( double )=4 s i z e o f ( i n t )=4 s i z e o f ( l o n g i n t )=4 The pointers are 4 bytes long, since the memory space is 32 bits 4 bytes * 8 bits/byte = 32 bits. The maximum memory size accessible to a program is thus 2 32 = 4294967300 bits = 4 GiB. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 13/45
sizeof, cont d However, on x86 64 the program results in s i z e o f ( f l o a t )=4 s i z e o f ( double )=8 s i z e o f ( i n t )=4 s i z e o f ( l o n g i n t )=8 s i z e o f ( f l o a t )=8 s i z e o f ( double )=8 s i z e o f ( i n t )=8 s i z e o f ( l o n g i n t )=8 Note that long int is 8 bytes, compared to 4 bytes on x86. The type is more accurate on x86 64 types in C are not portable. While int and float take only 4 bytes of memory, pointers to float take twice the amount of memory. The maximum amount of memory accessible on 64-bit architectures is 2 64 bits 1.717 10 10 GiB. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 14/45
Arrays Arrays in C are pointers to the start of the array. 7 * sizeof(double) double p[7]; p[0] p[1] p[2] p[3] p[4] p[5] p[6] p The following is completely valid: double p [ 7 ] ; double q=p ; Here q is now explicitely defined as a pointer. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 15/45
Multidimensional arrays Multidimensional arrays are arrays, the elements of which are arrays. double t[2][3]; t t[0][0] t[0][1] t[0][2] t[1][0] t[1][1] t[1][2] t[0] t[1] C stores multidimensional arrays by rows, i.e. the last index runs the fastest (opposite to Fortran). Scientific Programming in C, fall 2012 Susi Lehtola Pointers 16/45
Static vs dynamic allocation When an array has a fixed size, it is allocated statically during compilation. However, you may run into problems with the stack size if you have large static arrays (especially common in Fortran 77 programs for which dynamic allocation is not possible). If you get segfaults with large static arrays, check the stack size limit. In the BASH shell, set ulimit -s unlimited and try again. In Windows, the stack size is set during the linker phase, and you need to increase it with, e.g. -Wl, stack,8388608. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 17/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 18/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. 1. Use static allocation, defining the size of the array at compile time. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 19/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. 1. Use static allocation, defining the size of the array at compile time. 2. Use dynamic allocation to create a multidimensional array. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 20/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. 1. Use static allocation, defining the size of the array at compile time. 2. Use dynamic allocation to create a multidimensional array. 3. Use dynamic allocation to create a one-dimensional array, and handle the indexing yourself, e.g., with a macro. Which one is the fastest? Scientific Programming in C, fall 2012 Susi Lehtola Pointers 21/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. 1. Use static allocation, defining the size of the array at compile time. 2. Use dynamic allocation to create a multidimensional array. 3. Use dynamic allocation to create a one-dimensional array, and handle the indexing yourself, e.g., with a macro. Which one is the fastest? Macros are defined as, e.g., #define sq ( x ) ( ( x ) ( x ) ) Scientific Programming in C, fall 2012 Susi Lehtola Pointers 22/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. 1. Use static allocation, defining the size of the array at compile time. 2. Use dynamic allocation to create a multidimensional array. 3. Use dynamic allocation to create a one-dimensional array, and handle the indexing yourself, e.g., with a macro. Which one is the fastest? Macros are defined as, e.g., #define sq ( x ) ( ( x ) ( x ) ) Why the need for extra parentheses? Scientific Programming in C, fall 2012 Susi Lehtola Pointers 23/45
Multidimensional arrays, cont d There are three possibilities in C to handle multidimensional arrays. 1. Use static allocation, defining the size of the array at compile time. 2. Use dynamic allocation to create a multidimensional array. 3. Use dynamic allocation to create a one-dimensional array, and handle the indexing yourself, e.g., with a macro. Which one is the fastest? Macros are defined as, e.g., #define sq ( x ) ( ( x ) ( x ) ) Why the need for extra parentheses? Since the preprocessor just does string replacement, without the parentheses calling, e.g. sq(x+1) would result in x + 1 * x + 1, i.e., 2*x+1. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 24/45
Dynamic memory allocation Dynamic memory allocation in C is performed using the void m a l l o c ( s i z e t s i z e ) ; void c a l l o c ( s i z e t nmemb, s i z e t s i z e ) ; functions, defined in stdlib.h. void is a dummy datatype used to make the functions totally general. The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. The memory is set to zero. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 25/45
Dynamic memory allocation, cont d The memory that has been dynamically allocated must be freed when it is no longer used. This is done with the void f r e e ( void p t r ) ; function, also defined in stdlib.h. The free() function frees the memory space pointed to by ptr, which must have been reserved by malloc() or calloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no operation is performed. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 26/45
Dynamic memory allocation, cont d If you need to grow or reduce the amount of allocated memory, you can use the void r e a l l o c ( void ptr, s i z e t s i z e ) ; function, still defined in stdlib.h. The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 27/45
Dynamic memory allocation, example #i n c l u de < s t d l i b. h> i n t main ( void ) { / Amount o f e l e m e n t s i n the a r r a y / s i z e t N=1000; / P o i n t e r to the a r r a y, i n i t i a l i z e to n u l l / double p=null ; / A l l o c a t e memory f o r the a r r a y / p=m a l l o c (N s i z e o f ( double ) ) ; / Double the amount o f a l l o c a t e d memory / p=r e a l l o c ( p, 2 N s i z e o f ( double ) ) ; / Free the memory / f r e e ( p ) ; return 0 ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 28/45
Dynamic memory allocation, cont d Whenever you use dynamic allocation, be sure to check that the allocation was succesful, i.e. that the returned pointer is not NULL. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 29/45
Dynamic memory allocation, cont d In order for a function to return an array, you must use dynamic memory allocation. double a r r ( ) { double a [ 1 0 ] ; return a ; } This function returns a pointer to the start of a. However, a is a local variable, and thus when the value is returned, the array doesn t exist anymore. double a r r ( ) { double a=m a l l o c (10 s i z e o f ( double ) ) ; return a ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 30/45
Multidimensional arrays, dynamical allocation #i n c l u de < s t d l i b. h> i n t main ( void ) { / S i z e o f the a r r a y / s i z e t M=20, N=10; s i z e t i ; / P o i n t e r to the a r r a y o f p o i n t e r s / double p=null ; / A l l o c a t e memory f o r the p o i n t e r a r r a y / p=m a l l o c (M s i z e o f ( double ) ) ; / I n i t i a l i z e the p o i n t e r a r r a y / f o r ( i =0; i <M; i ++) p [ i ]= m a l l o c (N s i z e o f ( double ) ) ; / Now the e l e m e n t s can be a c c e s s e d as p [ i ] [ j ]. Free the memory / f o r ( i =0; i <M; i ++) f r e e ( p [ i ] ) ; f r e e ( p ) ; return 0 ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 31/45
Multidimensional arrays, dynamical allocation What does this look like in memory? int i; double **p=malloc(3*sizeof(double *)); for(i=0;i<3;i++) p[i]=malloc(4*sizeof(double)); double * double * double * p p[0] p[1] p[2] double double double double double double double double double double double double Scientific Programming in C, fall 2012 Susi Lehtola Pointers 32/45
Multidimensional arrays, dynamical allocation Allocation of the pointer arrays is done in small blocks, so the memory might be non-continguously allocated. If you do a lot of malloc()s and free()s of different sizes, you will quickly end up trashing your memory. Even though there is a substantial amount of free memory still left, it s in bits and pieces all over, and no large blocks are free. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 33/45
Multidimensional arrays, motivation for manual indexing Quite often one can run into situations, where manual indexing comes in handy. For example, the integrals that occur in quantum chemistry have an awful lot of indices. When a Gaussian basis set is used, integrals are calculated over functions χ GTO klm (r) = χgto k (x) χ GTO l (y) χ GTO m (z), k (x) = ( ) 2α 1/4 (4α) k π (2k 1) x k exp ( αx 2). χ GTO As you can see, every function has 3 indices (and a specific value of α). Functions are added in shells with k + l + m = constant, sharing a constant value of α. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 34/45
Multidimensional arrays, motivation for manual indexing The simplest integral is the overlap integral S ij = χ i (r)χ j (r)d 3 r Here i and j are compound indices, that include both the cartesian indices k, l, m and the exponent α. The integrals factorize in x, y and z, and since, e.g. x χ GTO k (x) = aχ GTO k 1 (x) + bχgto k+1 (x) recursion relations can be come up with. With the recursion relations it s possible to quickly calculate all of the wanted integrals. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 35/45
Multidimensional arrays, motivation for manual indexing Even the overlap integral has now 2 3 = 6 indices. Even harder are the electron repulsion integrals χ i (r)χ j (ij kl) = (r)χ k(r )χ l (r ) r r d 3 rd 3 r where the recursion relations now have 4 3 = 12 indices. For the overlap we just want a matrix, so we have to come up with a scheme for compounding the three cartesian indices into one. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 36/45
Multidimensional arrays, manual indexing Use of manual indexing instead of pointer arrays guarantees that the memory is continguous. If you want, you can also switch the storage order to match with that of Fortran. #i n c l u de < s t d l i b. h> #define i d x ( a r r, i, j ) ( ( j ) a r r.m+( i ) ) #define e l ( a r r, i, j ) ( a r r. p [ i d x ( a r r, i, j ) ] ) i n t main ( void ) { s t r u c t a r r a y { s i z e t M; s i z e t N; double p ; } ; s t r u c t a r r a y a ; a.m=10; a.n=20; a. p=m a l l o c ( i d x ( a, a.m, a.n) s i z e o f ( double ) ) ; f r e e ( a. p ) ; return 0 ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 37/45
Memory leaks When you use dynamic memory allocation in C, you have to make sure that you free the memory you have allocated otherwise you will be leaking memory. For example #i n c l u de < s t d l i b. h> i n t main ( void ) { s i z e t N=1000; double p=m a l l o c (N s i z e o f ( double ) ) ; / Memory l e a k h e r e / p=m a l l o c (2 N s i z e o f ( double ) ) ; f r e e ( p ) ; return 0 ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 38/45
Memory leaks You can find memory leaks with, e.g., Valgrind, which is available for Linux and MacOS X. $ valgrind./ memleak.x ==27142== Memcheck, a memory error detector ==27142== Copyright (C) 2002-2012, and GNU GPL d, by Julian Seward et al. ==27142== Using Valgrind -3.8.1 and LibVEX ; rerun with -h for copyright info ==27142== Command :./ memleak.x ==27142== ==27142== ==27142== HEAP SUMMARY : ==27142== in use at exit : 8,000 bytes in 1 blocks ==27142== total heap usage : 2 allocs, 1 frees, 24,000 bytes allocated ==27142== ==27142== LEAK SUMMARY : ==27142== definitely lost : 8,000 bytes in 1 blocks ==27142== indirectly lost : 0 bytes in 0 blocks ==27142== possibly lost : 0 bytes in 0 blocks ==27142== still reachable : 0 bytes in 0 blocks ==27142== suppressed : 0 bytes in 0 blocks ==27142== Rerun with --leak - check=full to see details of leaked memory ==27142== ==27142== For counts of detected and suppressed errors, rerun with : -v ==27142== ERROR SUMMARY : 0 errors from 0 contexts ( suppressed : 2 from 2) Scientific Programming in C, fall 2012 Susi Lehtola Pointers 39/45
Dynamically allocated memory In contrast to Fortran arrays, in C there s no way to find out how much memory has been allocated for an array you have to keep track of this yourself. Statically allocated arrays form an exception: #i n c l u de <s t d i o. h> #i n c l u de < s t d l i b. h> i n t main ( void ) { double p [ 1 0 ] ; double q=p ; double r=m a l l o c (10 s i z e o f ( double ) ) ; p r i n t f ( s i z e o f ( p)=%u\n, s i z e o f ( p ) ) ; p r i n t f ( s i z e o f ( q)=%u\n, s i z e o f ( q ) ) ; p r i n t f ( s i z e o f ( r)=%u\n, s i z e o f ( r ) ) ; f r e e ( r ) ; return 0 ; } Scientific Programming in C, fall 2012 Susi Lehtola Pointers 40/45
Dynamically allocated memory The output on x86 64 is $. / a. out s i z e o f ( p)=80 s i z e o f ( q)=8 s i z e o f ( r )=8 Even though q points to the static array, its size is still that of a pointer to double. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 41/45
Tables and pointers The [] operator is in principle not necessary for array access - pointer arithmetic can also be used for the same thing. #i n c l u de <s t d i o. h> #i n c l u de < s t d l i b. h> i n t main ( void ) { i n t i ; const i n t N=10; double p [N ] ; double q=p ; / H e l p e r / } f o r ( i =0; i <N; i ++) / D e r e f e r e n c e q, s e t the v a l u e to i and then i n c r e a s e q by one / ( q++)=i ; f o r ( i =0; i <N; i ++) p r i n t f ( %i %e \n, i, p [ i ] ) ; return 0 ; Scientific Programming in C, fall 2012 Susi Lehtola Pointers 42/45
Pointer arithmetic When a pointer is incremented (or decremented), it is moved forward (backward) by the size of its datatype. #i n c l u de <s t d i o. h> #i n c l u de < s t d l i b. h> i n t main ( void ) { / Get a random p o i n t e r / void p=m a l l o c ( s i z e o f ( double ) ) ; / Test p o i n t e r s / f l o a t f p=p ; double dp=p ; } f p++; dp++; p r i n t f ( p = %p\n, p ) ; p r i n t f ( f p = %p\n, f p ) ; p r i n t f ( dp = %p\n, dp ) ; f r e e ( p ) ; return 0 ; Scientific Programming in C, fall 2012 Susi Lehtola Pointers 43/45
Pointer arithmetic, cont d The result is, e.g. $. / a. out p = 0 x21d4010 f p = 0 x21d4014 dp = 0 x21d4018 Even though the pointers started from the same place, incrementation had different effects on them, because float is 4 bytes, whereas double is 8 bytes. Scientific Programming in C, fall 2012 Susi Lehtola Pointers 44/45
Pointers to structures When you have pointers to structures, you can access elements with the -> operator. #i n c l u d e <s t d i o. h> i n t main ( v o i d ) { s t r u c t c o o r d s { double x ; double y ; double z ; } ; s t r u c t c o o r d s r ; s t r u c t c o o r d s rp=&r ; } rp >x =1.0; rp >y =2.0; rp >z =3.0; p r i n t f ( r=(%f,% f,% f )\ n, r. x, r. y, r. z ) ; r e t u r n 0 ; The other option would be to write (*rp).x etc. The output is $. / a. out r =(1.000000,2.000000,3.000000) Scientific Programming in C, fall 2012 Susi Lehtola Pointers 45/45