0% found this document useful (0 votes)
46 views

Dynamic Memory Allocation

Dynamic memory allocation allows programs to request memory from the operating system at runtime using functions like malloc(). This memory is stored in the heap rather than on the stack. Malloc returns a void pointer to the allocated memory. Programmers must free this memory using free() to avoid memory leaks. Dynamic allocation provides flexibility over static allocation but is more error prone and can cause memory fragmentation over time.
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
46 views

Dynamic Memory Allocation

Dynamic memory allocation allows programs to request memory from the operating system at runtime using functions like malloc(). This memory is stored in the heap rather than on the stack. Malloc returns a void pointer to the allocated memory. Programmers must free this memory using free() to avoid memory leaks. Dynamic allocation provides flexibility over static allocation but is more error prone and can cause memory fragmentation over time.
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

dynamic memory allocation

variable variable length


we can ask the compiler to reserve a certain amount of memory for variables or strings (this memory is reserved compile time). suppose we make a universal text editor, then we could store the text as character strings in a two dimensional array:
the first index sets the line number, the second the character position.

since files can contain many lines, and lines can be long, we have to ask the compiler to reserve a significant amount of memory:
#define MAXNUMLINE 100000 #define MAXLINELENGTH 1024 char text[MAXNUMLINE][MAXLINELENGTH]; ... strcpy(text[0], Maxima rules!\0);

the above example would allocate a 100 Megabytes of memory, even though the text might just contain one line (albeit essential).

allocating memory run-time: malloc()


size is void * means this
returns a pointer to memory of undefined type

#include <stdlib.h> void * malloc (size);

an int

this allocates (reserves) size bytes of memory. the memory is not initialized.

malloc returns a pointer

to the first byte of the allocated memory space.

if there is no memory space left, malloc will return 0.

example: copying an arbitrary string


need to cast the return value type from void * to char * need to allocate the length of the string + 1 for the terminating \0

return a pointer to the new string

multiply by the size of a character (= 1 byte)

char * copy_string(char * str) { /* allocate just enough memory */ char * copy_of_str = (char *) malloc((strlen(str)+1)*sizeof(char)); if(copy_of_str == 0) return 0; /* return 0 case there is no memory */ strcpy(copy_of_str, str); /* copy the string into the new memory */ return copy_of_str; } the memory is not initialized. here we copy the string in it.

example: making a variable-size array


int * create_vector_array(int number_of_elements) { int j; int * v; v = (int *) malloc( number_of_elements * sizeof(int) ); if(v == 0) return 0; /* Houston, we have a problem */ for(j = 0; j < number_of_elements; j++) { v[j] = 0; } return v; }

example: create_vector_array(10)
10 * 4 = 40 bytes

v v[0] v[1] v[2] v[3] v[4] v[5] v[6] v[7] v[8] v[9]

example: the variable-sized matrix


// allocates an n_columns by n_rows array of doubles double ** create_matrix(int n_columns, int n_rows) { int col, row; double ** mtx = (double **) malloc( n_columns * sizeof(double *) ); for(col = 0; col < n_columns; col++) { mtx[col] = (double *) malloc( n_rows * sizeof(double) ); for(row = 0; row < n_rows; row++) { mtx[col][row] = 0.0; } }

example: create_matrix(5, 4)
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 mtx[col][3] mtx[col][2] mtx[col][1] mtx[col][0]

return mtx; }

double ** my_matrix = create_matrix(5,4); my_matrix[0][1] = 1.415; my_matrix[1][0] = -1.415; ...

0.0

mtx mtx[2] mtx[4] mtx[0] mtx[1] mtx[3]

releasing memory: free()


memory can be returned to the memory manager to be recycled: example, based on the malloc() examples on the previous slides:
char * name = copy_string(Huub van Doorne); int * my_vector = create_vector_array(100000); /* do something useful with them */ /* now we dont need them anymore: let us release the memory space */ free(name); free(my_vector);

you can free objects in any order. make sure that you are not using the pointer to the memory after it was freed and never call free() twice on the same pointer. to be sure that this can never happen, I always set the pointer to 0 after a free:
name = 0; my_vector = 0;

more tricky use of free()


every malloc() should have a corresponding free(). The code below will not free properly. Internally create_matrix() did 1001 calls to malloc()
double ** matrix = create_matrix(1000, 1000); // do something useful with it /* now we dont need it anymore: kill it */ free(matrix);

even worse, after the above free() the pointers to 1000 pieces of memory are lost forever. instead, we need to write a function to free all of them:
void delete_matrix(double ** matrix, int n_columns) { int col; for(col = 0; col < n_columns; col++) { free(mtx[col]); /* free each column */ } free(matrix); /* free matrix at the very end */ }

we need to know the number of columns, but not the number of rows.

how does the memory manager work?


void main() /* my little experiment: figuring out what malloc does */ { void * ptr[25]; /* array of void pointers to the reserved memory */ int j; for(j = 1; j < 20; j++) { ptr[j] = malloc(j); /* allocate j bytes of memory, put address in ptr[j] */ if(j == 1) printf("ptr[%2d] = %u reserved %2d bytes\n", j, (unsigned) ptr[j], j); else printf("ptr[%2d] = %u reserved %2d bytes -> used = %u\n", j, (unsigned) ptr[j], j, (unsigned)ptr[j] - (unsigned)ptr[j-1]); } free(ptr[4]); /* free a block of 4 bytes */ ptr[20] = malloc(4); /* allocate one of 4 bytes again */ printf("ptr[20] = %u\n", (unsigned) ptr[20]); ptr[ 1] = 8069796 reserved 1 bytes }
ptr[ 2] ptr[ 3] ptr[ 4] ptr[ 5] ptr[ 6] ptr[ 7] ptr[ 8] ptr[ 9] ptr[10] ptr[11] ptr[12] ptr[13] ptr[14] ptr[15] ptr[16] ptr[17] ptr[18] ptr[19] ptr[20] = = = = = = = = = = = = = = = = = = = 8069812 8069828 8069844 8069860 8069876 8069892 8069908 8069924 8069940 8069956 8069972 8069988 8070008 8070028 8070048 8070068 8070092 8070116 8069844 reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved reserved 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes bytes

observation 1: the memory manager


reserves at least 16 bytes, even if we ask for much less.

observation 2:
the memory manager needs 3 bytes for administration.

observation 3:
the memory manager indeed recycles memory that was freed.

-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->

used used used used used used used used used used used used used used used used used used

= = = = = = = = = = = = = = = = = =

16 16 16 16 16 16 16 16 16 16 16 16 20 20 20 20 24 24

heap memory: memory fragmentation


dynamic memory allocation is much harder than memory allocation using a stack pointer. after a repeated malloc and free, the heap memory could be fragmented, and not contain enough space for a big chunk of memory. modern memory managers do a decent job, but behind the scenes this takes effort (run-time and memory)

compare with stack memory


the top of the memory stack is maintained by a stack pointer in the CPU. no overhead, and no run-time administration. no fragmentation. pow fabs() y help x help safe_pow() y x c main() b a
#include <stdio.h> #include <math.h> double safe_pow(double x, double y); void main() { int a; for(a = -2; a <= 2; a++) { int b; for(b = 0; b < 4; b++) { double c = safe_pow(a, b); printf(%d^%d = %f\t, a, b, c); } printf(\n); } } double safe_pow(double x, double y) { double help; help = fabs(x); help = pow(help, y); return help; }

stack and heap: whats the difference?


string as stack variable:
void myroutine() { char name[7] = Maxima; char * namep = name; /* do something */
the string dies automatically at the end of the routine.

string as heap variable:


void myroutine() { char * namep = malloc(7*sizeof(char)); strcpy(namep, Mabel); /* do something */ free(namep); }
the string must be freed explicitly. if you dont, the memory space will be lost forever.

the good news: fast more memory efficient no memory leakage the bad news: length/size fixed must determine compiletime

the bad news: slower, fragmentation issue some memory wasted error-prone, memory leakage the good news: length/size flexible size set run-time, adapt to requirements

malloc()s family: calloc() and realloc()


calloc also allocates memory:
void * calloc (size_t count, size_t size);

this will allocate count times size bytes of memory; the difference with malloc() is that calloc() sets the memory to 0.

realloc changes the size of a memory block:


void * realloc (void * block, size_t new_size);

behind the scenes this will allocate a new block of new_size bytes, and copy the values of the existing block in the new memory; finally, the existing memory block will be freed, so dont use block afterwards!
char * name = copy_string(Mabel); /* allocates 6 bytes */ name = realloc(name,25); /* enlarge the block to 25 bytes */ strcat(name, and Klaas); /* stick and Klaas behind it */ printf(%s, name); Mabel and Klaas

dynamic memory management issues


dynamic memory allocation is difficult you have to know exactly what you are doing. free every byte that you malloced! memory leakage is a major problem for big programs. dynamic memory allocation is error prone. manual casting of pointer allow easy mistakes. free is called at a very different spot as malloc. memory problems are often not caught by the compiler. dynamic memory allocation can be slow. a complex memory manager works behind the scenes to recycle memory efficiently. use normal stack variables if you can. dynamic memory allocation causes some memory overhead. especially on small chunks of memory. after a while, memory will look like Swiss cheese, leaving no room for larger blocks.

You might also like