Dynamic Storage Allocation

The malloc() function provides dynamically-allocated storage.

The malloc() function sets aside a contiguous chunk of bytes of memory and returns the address of this chunk to be stored in a pointer.

The calloc() function sets aside a block of memory in the same fashion as malloc(), but it also zero-initializes the memory, meaning that the allocated memory is filled with 0-bits. The calloc() function also takes two arguments, whereas malloc() takes only one. The calloc() function allocates the number of bytes produced by the product of the two arguments it receives.

#include "stdafx.h"
#include <stdlib.h>

int main()
{
 //allocate 1 int's worth of memory
 int *i = (int *)malloc(sizeof(int));
 //allocate 5 double variable's worth of memory
 double *dArray = (double *)malloc(sizeof(double) * 5);

 //let's use calloc()
 char *szText = (char *)calloc(64, sizeof(char));
 int *i2 = (int *)calloc(1, sizeof(int));

 //will display 'garbage' value
 printf("Allocated with malloc(), *i = %d\n", *i);
 //will display 0
 printf("Allocated with calloc(), *i2 = %d\n", *i2);

 return 0;
}

Allocated memory storage is on the heap. Traditionally, the heap started at the low address of the process and grew upwards, whereas the stack started at a high address and grew downwards. Occasionally, a stack-heap collision would occur, where the stack or the heap grew overwrote one or the other.  As memory on modern OSes is pageable virtual memory, this is no longer a concern. At this point, even the idea of the stack growing downward and the head growing upward is conceptual rather than literal.

If the allocated memory function asks for more bytes than are available on the heap, the malloc() or calloc() function returns a NULL pointer. So, by checking the pointer with NULL, we can ensure that the memory was allocated.

#include "stdafx.h"
#include <stdlib.h>

int main()
{
 int *iPtrArray = (int*)malloc(10 * sizeof(int));

 if (iPtrArray != NULL) {
 printf("Memory allocated.\n");
 }

 return 0;
}

When allocated memory has been used and is no longer needed, we can return it to the heap with the free() function. Memory that has been allocated dynamically should be free, otherwise we will end up with memory leaks.

Now, when a process ends, all of the memory is reclaimed by the system . Therefore, it is not necessarily a big deal if we do not call free() in the small programs we have been writing. However, it’s best to maintain the ironclad habit of free-ing all memory that has been dynamically allocated.

 #include "stdafx.h"
#include <stdlib.h>

int main()
{
 double *dPtrArray = (double *)malloc(sizeof(double) * 5);

 if (dPtrArray != NULL) {
 printf("Memory allocated!\n");
 }

 //free the memory
 free(dPtrArray);
 dPtrArray = NULL;

 return 0;
}

After calling free on a pointer, we should consider the pointer to have an undefined value. Setting the value of the pointer to NULL explicitly is a good practice. A dangling pointer can result if we try to access deallocated memory via a pointer.

Advertisements

Allocating Memory Storage at Runtime in C

The C library has functions for allocating memory storage space at runtime via a process called dynamic memory allocation. The advantage of dynamic memory allocation over static memory allocation is that we are not required to know how how much memory the program will need before executing the program. All of Cs dynamic memory allocation functions are stored in stdlib.h.

There are four dynamic memory allocation functions that we should know: calloc(), malloc(), realloc(), and free(). The two core functions are malloc() and free(). Each time malloc() is called, a section of free memory is allocated from the heap. Each time free() is called, memory is returned to the system’s heap.

Since we haven’t looked at it previously, first let’s work with calloc(). The calloc() function takes two arguments, whereas the malloc() function takes only one. The first argument is the number of items to allocate memory for, and the second argument is the size of each item in memory. The calloc() function returns a pointer to the first byte of the allocated memory. If there is not enough memory, a null pointer is returned.

#include <stdio.h>
#include <stdlib.h>

double *getMem(int numElements);

int main(void){
    
    int i = 0;

    double *ptrToDoubles = getMem(100);
    
    double *ptrToStart = ptrToDoubles;
    
    while(i++ < 100){
        *(ptrToDoubles++) = i * .1587;
    }

    printf("double Array begins at location %p\n", ptrToStart);
    printf("and ends at location %p\n", ptrToDoubles);    

    ptrToDoubles = ptrToStart;

    //print out first item in 
    //dynamic double array
    printf("first element is %f\n",*ptrToDoubles);

    //print out last item in 
    //dynamic doubles array
    printf("last element is %f\n", *(ptrToDoubles+99));

    //free the memory!
    free(ptrToDoubles);
}

double *getMem(int numElements){

    double *ptr;
    
    ptr = calloc(numElements, sizeof(double));

    if(!ptr){
        printf("Allocation error.\n");
        exit(1);
    }

    return ptr;

}

As we have seen above, the free() function returns the memory pointed to by its argument to the heap.

Rather than allocating a group of bytes as malloc() does, calloc() allocates a group of data elements of a set size.

#include <stdio.h>
#include <stdlib.h>


int main(void){
    
    //number of elements to create
    unsigned n;
    //index num
    int i;

    int value;    

    int *p;

    printf("How many numbers of type int do you want to allocate?\n");

    scanf(" %u", &n);

    p = calloc(n, sizeof(int));

    if(p!=NULL){
        printf("Memory allocation successful!\n");
    } else {
        printf("Memory allocaion failed!\n");
        return 1;
    }

    while(1){
        printf("What element would you like to assign a value to?");
        printf("(Enter negative number to finish assigning values)\n");

        scanf(" %d", &i);
        if(i < 0){
            break;
        }

        //check if index is within range
        if(i >= n){
            printf("Index out of range.\n");
            continue;
        }

        printf("What value would you like to assign to it?\n");
        scanf(" %d", &value);

        *(p+i) = value;

    }

    //print out values
    i = 0;
    printf("The dynamic int array contains: \n");
    while(i < n){
        printf("%d\n", *(p+i));
        i++;    
    }

    free(p);

    return 0;

}

Note that unlike malloc(), the calloc() function also initializes the stored memory to all zeros, which means that calloc() is slightly slower than malloc().

The malloc() function returns a pointer to the first byte in memory of the size specified as its argument. If there is insufficient memory, the malloc() function will return a null pointer. We should verify that the return value is not null before attempting to use it.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct name{
    char firstName[50];
    char lastName[50];    
};

//returns pointer to name struct
//exits on failure to allocate
struct name *getNameStruct(void){
    struct name *ptr;
    
    if((ptr = malloc(sizeof(struct name)))==NULL){
        printf("Allocation Error.\n");
        exit(1);
    }

    return ptr;

};

int main(void){

    struct name *myName = getNameStruct();
    
    strcpy(myName->firstName, "Al");
    strcpy(myName->lastName, "Jensen");

    printf("First Name %s\n", myName->firstName);
    printf("Last Name %s\n", myName->lastName);

    free(myName);

    return 0;

}

The realloc() function changes the size of a block of memory that was previously allocated by malloc() or calloc(). The first argument is a pointer to the original block of memory. The second argument is the size, in bytes, of the new block.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void){
    
    char *ptr1;
    char *ptr2;
    char *ptr3;

    ptr1 = malloc(18 * sizeof(char));

    if(!ptr1){
        printf("Allocation Error.\n");
        return 1;
    }

    strcpy(ptr1, "This is 17 chars.");

    printf("%s\n", ptr1);

    ptr2 = malloc(22 * sizeof(char));

    if(!ptr2){
        printf("Allocation Error.\n");
        return 1;
    }

    strcpy(ptr2, "But this is 21 chars.");    

    ptr3 = realloc(ptr1, 40);
    
    strcat(ptr3, ptr2);

    printf("%s\n", ptr3);    

    //free memory
    //note that ptr3 is reallocated
    //storage of ptr1
    free(ptr2);
    free(ptr3);

    return 0;

}

if you can, please take the time to visit my Amazon.com author page at  http://www.amazon.com/Al-Jensen/e/B008MN382O