ECE 103 Engineering Programming Chapter 47 Dynamic Memory Alocation Herbert G. Mayer, PSU CS Status 6/4/2014 Initial content copied verbatim from ECE 103 material developed by Professor Phillip PSU ECE
Syllabus Memory Allocation Functions Creating a 1-D Array at Runtime Creating a 2-D Array at Runtime calloc, malloc, free Dangers
2 Memory Allocation Functions The size of arrays must be known at compile-time (C90 limitation only; C99 supports VLAs). A C program can also make a request at runtime for a memory block of arbitrary size. If the memory block is no longer needed, the system can be told to release the memory. To use C’s memory allocation functions, add #include to the source file.
3 calloc void * calloc (size_t N, size_t bsize); Allocates a block of memory from the heap at runtime to use for data storage. Block can contain N items, each of size bsize. Block is initialized to zero. If successful, returns a pointer to the allocated memory. If unsuccessful, returns the NULL value. Storage persists until explicitly freed or the program terminates. Example: Create a block of storage for 512 integers at runtime. int *p; p = (int *) calloc(512, sizeof(int));
4 malloc void * malloc (size_t bsize); Allocates a block of memory from the heap at runtime to use for data storage. Block is of size bsize. Block is not initialized to zero (i.e., indeterminate values). If successful, returns a pointer to the allocated memory. If unsuccessful, returns the NULL value. Storage persists until explicitly freed or the program terminates. Example: Create a 1024 character memory block at runtime. char *q; q = (char *) malloc(1024 * sizeof(char));
5 realloc void * realloc (void *p, size_t bsize); Changes the size of a previously allocated memory block. p is a pointer to the existing block. bsize is the desired new size. If successful, returns a pointer to the resized block. If unsuccessful, returns the NULL value. Example: Suppose p points to a 1024 char block created by malloc. Change the block’s size to p_new = (char *) realloc(p, 2048*sizeof(char));
6 free void * free (void *p); Releases memory that has been previously allocated by calloc, malloc, or realloc back to the heap. p is a pointer to the existing block. If p is NULL, then free does nothing. Example: Suppose p points to a dynamically allocated memory block. The storage is no longer needed, so free up the memory. free(p);
7 Potential Dangers … Dynamically allocated memory exists until it is released by free, or until the program terminates. If a pointer “loses track” of a memory block, and there are no other pointers still tracking that block, then the block becomes inaccessible. Inaccessible blocks still continue to use memory for as long as the program is running (memory leak).
8 mem… void * memset (void *ptr, int value, size_t num); Sets first num bytes of the memory block pointed by ptr to the specified value. void * memcpy (void *dst, const void *src, size_t num); Copies the values of num bytes from the location pointed by src to the memory block pointed by dst. No overlap is allowed. Returns dst. void * memmove (void *dst, const void *src, size_t num); Copies the values of num bytes from the location pointed by src to the memory block pointed by dst. Overlap is allowed. Returns dst. int memcmp (const void *ptr1, const void *ptr2, size_t num); Compares the first num bytes of the memory block pointed by ptr1 to the first num bytes pointed by ptr2. Returns zero if equal, or non-zero otherwise. void * memchr (const void *ptr, int value, size_t num); Searches within the first num bytes of the memory block pointed by ptr for the first occurrence of value. If found, returns pointer to location, or NULL otherwise.
9 Creating a 1-D Array at Runtime When declaring a normal array variable, the size of the array must be specified at compile-time (C90). Using dynamic memory allocation, an array can be created whose size is determined at runtime. The size of the desired block must be passed to either calloc or malloc. The sizeof() operator can determine the block size (in bytes) of a given datatype.
10 The pointer returned by calloc or malloc must be cast to the correct datatype. The pointer should always be tested for NULL! The allocated array can be accessed using array notation. A memory block should be freed when it is no longer needed.
11 Example: Create a 1-D array at runtime that holds up to 10 elements of datatype float. float *A; A = (float *) calloc(10, sizeof(float)); Pointer to the allocated memory block that will hold the 1-D array values. Desired 1-D array size Desired datatype Calculates size of datatype (in bytes) Casts the pointer returned by calloc to the correct datatype
12 Example: #include int main (void) { float *A;/* Pointer to 1-D array */ int R;/* Size of the array */ int i;/* Index */ printf("Enter size of the array: "); scanf("%d", &R); /* Create the array at runtime */ if ((A = (float *) calloc(R, sizeof(float))) == NULL) { printf("Error: Could not allocate memory block.\n"); exit(0); } /* Can now access using array notation */ for (i = 0; i < R; i++) A[i] = i; free(A);/* Free up the block */ return 0; }
13 Suppose the entered array size is R = 5 and the system uses 64 bit pointers and 32 bit float values. Assume that calloc returns 2000 as the start address of the allocated memory block. IndexAddressContents A →A →
14 Creating a 2-D Array at Runtime Creating a 1-D array using runtime memory allocation requires a single level of indirection (e.g., double * ). To create a 2-D array, two levels of indirection are required (e.g., double ** ). In other words, a pointer to a pointer is required.
15 The 2-D array is created in two phases: The rows are created first. The return value is a pointer to a pointer. For each row, columns are created. The return value in each case is a pointer. The pointers should always be tested for NULL! The block is released in reverse order, i.e., the columns are freed first and then the rows.
16 Example: Create a 2-D array (size: 5 rows by 3 columns) at runtime that holds elements of type float. float **A; /* Create array of pointers to the rows */ A = (float **) calloc(5, sizeof(float *)); /* Create the rows */ for (i = 0; i < 5; i++) A[i] = (float *) calloc(3, sizeof(float)); Pointer to the allocated row block that contains pointers to the column blocks
17 Example: #include int main (void) { float **A;/* Pointer to 2-D array */ int R = 5, C = 3;/* Row and column sizes of the array */ int i, j;/* Row and column indices */ /* Create the rows of the 2-D array */ if ((A = (float **) calloc(R, sizeof(float *))) == NULL) { printf("Error: Cannot allocate memory block.\n"); exit(0); } /* For each row, create the columns */ for (i = 0; i < R; i++) if ((A[i] = (float *) calloc(C, sizeof(float))) == NULL) { printf("Error: Cannot allocate memory block.\n"); exit(0); } /* Can now access using array notation */ for (i = 0; i < R; i++) for (j = 0; j < C; j++) A[i][j] = i+j; for (i = 0; i < R; i++) free(A[i]);/* Free up the columns a row at a time */ free(A); /* Free up the rows */ return 0; }
18 Suppose that R = 5, C = 3 and the system uses 64 bit pointers and 32 bit float values. Assume calloc returns 2000 as the start address of the row block. Suppose that when creating columns, calloc returned 3000, 3012, 3024, 4000, and 4012 for the start addresses of the column blocks. Note: The non-contiguous allocation may be inefficient. Some prefer using a 1-D allocated block and manually calculating 2-D offsets. Row IndexAddressContents A →A → Column index Address
19 VLA vs. Dynamic memory allocation What are some pros and cons of using C99’s variable- length arrays versus dynamic memory allocation? + VLAs are easy to define + No pointers to create + Automated memory management (no need to free) − VLAs are stored on the stack → Stack size is limited. Large VLA may overrun space. − Not portable to all compilers C99