Programming Pointers, Multi-dimensional Arrays and Memory Management
Summary } Computer Memory } Pointers } Declaration, assignment, arithmetic and operators } Casting and printing pointers } Relationship with arrays } Multidimensional arrays } 2D examples } Memory management } Malloc/calloc/realloc } Vectors of pointers 2
Computer Memory } The minimum addressable unit is a byte } Bytes are combined to make integers and floating point values } Memory addresses in computers are often 32 bits (or nowadays, 64-bits) long, e.g. 0x7ffffa8c (hexadecimal) int (21) float (18.2) 3 Memory addresses!
Physical and Virtual Memory } Physical memory: physical resources where data can be stored and accessed by the processing unit } Cache, RAM, hard disk, removable storage } Virtual memory: abstraction by OS, addressable space accessible by your code } How much virtual memory can you have? } Typically 2 GB for Windows, 3-4 GB for Linux } Virtual memory maps to different parts of physical memory } Usable parts of virtual memory: stack and heap } stack: memory space for declared variables } heap: memory space for dynamic memory 4
Pointers } A pointer is just a memory location } A memory location is simply an integer value that can be interpreted as an address in memory } Address can be used to access/modify a variable from anywhere } Extremely useful, especially for data structures } The contents at a particular memory location are just a collection of bits } You must tell the compiler how the bits should be interpreted } Is this... an int value?... a pointer to a memory address?... a series of char values? 0xfe4a10c5 5
Pointer Variables } A pointer variable is just a variable, that contains a value that can be interpreted as a memory address } An uninitialized int variable holds some arbitrary garbage value } An uninitialized pointer variable points to some arbitrary garbage address } What happens when an arbitrary address is followed? } Depends on the arbitrary memory address! } In case the address lies in memory not allocated by the program, a segmentation fault may occur char *pm; 6 pm (char *) Memory Address Address Variable Value 0x4520 m 181 0x4524 pm 0x4520 Pointer Value
Addressing Variables } Every variable residing in memory has an address! } Address of a variable of type t has type t* } Just like other variables, they must be declared before use } What doesn t have an address? } Register variables } Constants/literals/preprocessor defines } Expressions } How to find an address of a variable? } The & operator! } & operator means address of } Read it as at 7
Pointer Declaration and Initialization } Declaration: } int *pa; /* not initialized: points to a random location */ } Declaration and initialization: } int a = 2; /* a is an integer */ } int *pa = &a; /* pa is a pointer containing the address of a */ } Usage } scanf( %d, pa); /* Don t have to put & before */ } pa is a pointer variable that stores the address of an integer variable a pa 8
Printing Pointers int a = 21; int *pa = &a; printf("%d\n", a); printf("%x\n", a); printf("%x\n", &a); printf("%x\n", pa); printf("%d\n", *pa); printf("%x\n", &pa); Output 21 15 bfee861c bfee861c 21 bfee8618 %x prints an hexadecimal value %p prints the pointer value Operators: & address of * dereference 9
Example Code: What is the Output? #include <stdio.h> Address Memory Name int main() { int a = 15, b = 38; int *c = &a; printf("%p : %d\n", &a, a); printf("%p : %d\n", &b,b); printf("%p : %p : %d\n", &c, c, *c); 0xeffffa94 0xeffffa90 0xeffffa8c 0xeffffa94 15 38 0xeffffa94 15 49 a b c a a = 49; printf("%p : %d\n", &a, a); printf("%p : %d\n", &b,b); printf("%p : %p : %d\n", &c, c, *c); 0xeffffa90 0xeffffa8c 38 0xeffffa94 b c } c = &b; printf("%p : %d\n", &a, a); printf("%p : %d\n", &b,b); printf("%p : %p : %d\n", &c, c, *c); 0xeffffa94 0xeffffa90 0xeffffa8c 15 49 38 0xeffffa90 a b c 10
NULL Pointer Value } There is a special pointer value NULL, that means pointing to nothing. Same as value 0. char *m = NULL;... if (m) {... safe to follow the pointer... } } Here, m is used as a Boolean value } If m is false, aka 0, aka NULL, it is not pointing to anything } Otherwise, it is (presumably) pointing to a valid address } Note: It is up to the programmer to assign NULL values when necessary 11
Indirection Operator * } I have a pointer what to do? } Pointer allows to obtain the contents of an address } Dereferenced pointer is like any other variable char *m = dog ; char result = *m; printf ( %c, *m); d o g \0 } m is an address of a char } *m allows to obtain the content of that address } result assumes the value d (char *) m d result 12
Address Operator & } Instead of the contents, returns the address! char *m = dog ; char result = *m; d o g \0 char *pr = &result; char **ppm = &m; (char *) m d result } pr needs a value of type char * } points to a char } ppm needs a value of type char ** (char **) ppm (char *) pr } points to a pointer of char 13
Pointer Assignment I } The assignment operator (=) may be used to copy pointers of the same type } Assume the following declaration: } int i, j, *p, *q; } Example of pointer assignment: } p = &i; // p points to variable i } q = p; // q points to the same place as p p? i p? i q q 14
Pointer Assignment II } If p and q both point to i, we may change i by assigning a new value to either *p or *q: *p = 1; p 1 i *q = 2; q p 2 i q } Any number of pointer variables may point to the same variable
Pointer Assignment III } The following assignment statements are NOT the same: q = p; *q = *p; } The first statement is a pointer assignment } The second statements assigns the value of the data that is pointed-to by p to the data that is pointed-to by q
Pointer Arithmetic I } C allows pointer values to be incremented by integer values char *m = dog ; d o g NUL char result = *(m + 1); } m is an address of a char } (m + 1) allows to obtain the next address } *(m + 1) allows to obtain the contents of the address of the next char } result gets the value o (char *) m o result 17
Pointer Arithmetic II } A slightly more complex example: char *m = dog ; d o g NUL char result = *++m; } m is an address of a char (char *) } ++m changes m to the address one byte higher } Careful! we have lost our pointer to the beginning of the array!!! } *++m allows to obtain the contents of that location } result gets the value o m o result 18
Pointer Arithmetic III } For pointers, only addition and subtraction are allowed } Other arithmetic operations don t make much sense } Pointers arithmetic depend on the type of data that the pointer has } Different data types have different sizes int a[2] = {17, 42}; int *m = a; int result = *++m; 17 (int) 42 (int) 19 (int *) m 42 (int) result
Pointer Arithmetic: Examples int list[] = {1, 2, 3, 4}; int *p = list; /* same as p = &list[0] */ printf( %x,p); /* prints ffe2de0c */ p = p + 1; /* p increases by 4 */ printf( %x,p); /* prints ffe2de10 */ double list2[] = {1.0, 2.0, 3.0}; double *p = list2; /* same as p = &list2[0] */ printf( %x,p); /* prints ffe2de0c */ p = p + 1; /* P increases by 8 bytes */ printf( %x,p); /* prints ffe2de14 */ 20
Casting Pointers } Can explicitly cast any pointer type to any other pointer type ppi = (double *)pn; /* pn originally of type ( int *) */ } Dereferenced pointer has new type, regardless of real type of data } Implicit cast to/from void * also possible } May cause segmentation faults and other difficult-to-identify errors 21
Arrays and Pointers Share Similarities } Name of the array is a pointer to the first element of an array } &aarray[0] is the same as aarray, points to the 1 st array element } After assigning the address of the first element of aarray, it is possible to access other elements of aarray: int aarray[100] = {0}; parray = &aarray[0]; *(parray + 1) = 20 // assign 20 to 2nd element of aarray *(parray + 4) = 30; // assign 30 to 5th element of aarray parray = &parray[3]; *(parray + 1) = 50; // assign 50 to 5th element of aarray 22
Pointer and [] } Any pointer to a block of memory can use the [] syntax, even if it is not declared as an array! } int *v; and int v[]; are the same thing int *p, int list[]={1,2,3,4}; p = list; // prints 3 printf( %d\n, p[2]); // two equivalent statements printf( %d, list[2]); printf( %d,*(list+2)); P List 1 2 3 4 23
Pointers and Arrays: Example #define N_VALUES 5 float values[n_values]; (float []) values &values[0] 0 0 0 &values [N_VALUES] (float) (float) (float) (float) (float) 0 0 (done!) vp (float *) float *vp; for ( vp = &values[0]; vp < &values[n_values]; ) *vp++ = 0;
Example: strcpy string copy char *strcpy(char *dest, const char *src) } src points to a sequence of char values that must be copied to dest, terminated by \0 } dest points to a portion of memory large enough to hold the copied chars } strcpy copies the char values of src to the memory pointed to by dest } strcpy also gives dest as a return value 25
Example: strcpy string copy char *strcpy(char *dest, const char *src) { } const char *p; char *q; for(p = src, q = dest; *p!= '\0'; p++, q++) *q = *p; *q = '\0'; return dest; (char *) src d o g NUL p (char *) (char *) q (char *) dest d o g NUL 26
Review: Pass by Value vs Pass by Reference } How to return more than one value? Pass by Reference! } Must use pointers in the calling function } The address of the value to be modified is passed as argument } & operator must be used int x, y; x = 5; y = sum2(x); int x,y; x = 5; y = 10; sumdiv(&x,&y); printf( %d %d\n,x,y); int sum2(int t) { return (t + t); } &x and &t are different x value is copied to t void sumdiv(int *_x, int *_y) { *_x = *_x + *_y; *_y = *_x / *_y; } _x and &x are equal _y and &y are equal 27
2D Arrays } Declaration: } int mymatrix[row_size][col_size]; } Declaration and Initialization: } int mymatrix[4][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16} }; Column 0 1 2 3 Row 0 1 2 1 2 3 4 5 6 7 8 9 10 11 12 mymatrix[0][1] 2 3 13 14 15 16 mymatrix[2][3] 12 28
Physically in One Memory Block } int mymatrix[2][4] = { {1,2,3,4},{5,6,7,8} }; } mymatrix: pointer to the first element of the 2D array (position [0][0]) } mymatrix[0]: pointer to the first row of the 2D array (it is a vector) } mymatrix[1]: pointer to the second row of the 2D array } &mymatrix[1] is the address of element mymatrix[1][0] } &mymatrix[0][0] is the address of 1 st element (same as mymatrix) ffe2de0c ffe2de10 ffe2de14 ffe2de18 ffe2de1c ffe2de20 ffe2de24 ffe2de28 1 2 3 4 5 6 7 8 row 1 row 2 Array elements are stored in row major order Row 1 first, followed by row2, row3, and so on 29
Functions of Multi-dimensional Arrays #define ROWS 3 #define COLS 5 int table[rows][cols]; // some processing... display(table); Arrays are always passed by reference After all, table is a pointer! void display( int x[][cols] ) To pass a multi-dimensional { array, the first array size does for (int i=0; i < ROWS; i++) not have to be specified but { the second (and any next) for (int j=0; j < COLS; j++ ) dimensions must be given! { printf( x[%d][%d]: %d\n", i, j, x[i][j]); } printf("\n"); } printf("\n"); } 30
Dynamic Memory } Vectors: Size must be known before compilation } Example: int vect[200]; int n = 300; int vect[n]; //not possible to do! } How to create vectors that are dynamic? } Must use pointers } int *vect = (int *) calloc(n, sizeof(int)); 31
Memory Management: Stack Allocated } When a function is called, memory is allocated for all of its parameters and local variables } Each active function call has memory on the stack (with the current function call on top) } When a function call terminates, the memory is deallocated ( freed up ) } Ex: main() calls f(), f() calls g() g() recursively calls g() g() g() f() main() 32
Memory Management: Heap Allocated } This is used for persistent data, that must survive beyond the lifetime of a function call } Global variables } Dynamically allocated memory C statements can create new heap data } Heap memory is allocated in a more complex way than stack memory } Like stack-allocated memory, the underlying system determines where the new memory comes from 33
Allocating Heap Memory void *malloc(size_t size); void *calloc(size_t num_elements, size_t element_size); } malloc: allocates a block of size bytes } calloc: allocates a block of num_elements * element_size bytes } Both functions return a pointer to the block } Note: void* denotes a generic pointer type } Both functions return NULL if unable to allocate memory } It is needed to cast the return value of malloc/calloc: } char *p = (char *) malloc(buffer_size); 34
Reallocating Heap Memory void *realloc(void *ptr, size_t new_size); } Given a previously allocated block starting at ptr } Change the block size to new_size } Return pointer to resized block } If block size is increased, contents of old block may be copied to a completely different region } In this case, the pointer returned will be different from the ptr argument, and ptr will no longer point to a valid memory region } If ptr is NULL, realloc is identical to malloc 35
Deallocating Heap Memory void free(void *pointer); } Given a pointer to previously allocated memory } Put the block back in the heap of unallocated memory in order to be reused } Note: easy to forget to free memory when it is no longer needed... } This is the source of the notorious memory leak problem } Difficult to trace the program may execute correctly for some time, until suddenly there is no more memory! 36
Safer Malloc } If there is no memory available, call to malloc might fail! } Easy to forget to check for this! void *alloc(size_t size) { void *new_mem; new_mem = malloc(size); if (new_mem == NULL) exit(1); return new_mem; } 37
Common Memory Errors } Using memory that was not initialized } Uninitialized memory read or copy } Attempts to read/write contents of a NULL pointer } Very common error } Using memory that was not allocated in your program } Pointer arithmetic may lead to memory not allocated by you } Invalid pointer read/write due to memory management (e.g. realloc) } Using more memory than it was allocated } Access of memory that has been freed earlier } Using memory that it was allocated in the stack, but already freed } Reading an array out of its limits (bounds) } Memory leak: discarding a pointer without freeing memory! 38
Pointer of Pointers } Address stored by pointer is also data in memory } The location of an address stored in memory can be addressed using a pointer to that pointer } int n = 4; } int *pn = &n; } int **ppn = &pn; } Many uses: } Image storage } Vectors of strings 39
Pointer Arrays } Pointer array array of pointers } int arr [20]; // an array of pointers to integers } char arr [10]; // an array of pointers to chars } Array of strings: each string stored as a pointer to an array of chars } Each string may be of different length char str1[] = "hello"; / length = 6 / char str2[] = "goodbye"; / length = 8 / char str3[] = "ciao"; / length = 5 / char strarray[] = {str1, str2, str3}; } Note: strarray contains only pointers, not the characters themselves! 40
Pointer Arrays: Example #include <stdio.h> int main(void) { char *arr[3]; char *p1 = Delhi"; char *p2 = Kolkata"; char *p3 = "India"; arr[0] = p1; arr[1] = p2; arr[2] = p3; printf("\n p1 = [%s] \n",p1); printf("\n p2 = [%s] \n",p2); printf("\n p3 = [%s] \n",p3); printf("\n arr[0] = [%s] \n",arr[0]); printf("\n arr[1] = [%s] \n",arr[1]); printf("\n arr[2] = [%s] \n",arr[2]); } 41 return 0;
Dynamic Pointer Arrays #include <stdlib.h> int **array; array = malloc(nrows*sizeof(int *)); if(array == NULL) { fprintf(stderr, "out of memory\n"); exit(-1); } for(i = 0; i < nrows; i++) { array[i] = malloc(ncolumns*sizeof(int)); if(array[i] == NULL) { fprintf(stderr, "out of memory\n"); exit(-1); } } } Straightforward to call malloc to allocate a block of memory with a size computed at runtime } Can the same sort of thing being used to create multidimensional arrays? } Must use pointers of pointers! 42
To Review } Marques de Sá } Capítulo 5.6 e Capítulo 9.1 } Damas } Capítulo 8 } Capítulo 12 } Kernighan and Ritchie } Chapter 5 43