Pointers and Dynamic Memory

The malloc() function allocates a certain number of bytes of heap storage for our use. We specify the size of the memory block to allocate in bytes using a value of type size_t, which is an alias of an unsigned integer type. The function returns a pointer to the allocated memory block.

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

int main()
{
 size_t NumBytes = 256;
 void *ptrMemBlock;

 ptrMemBlock = malloc(NumBytes);

 if (ptrMemBlock == NULL) {
 printf("Failed to allocated memory.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Allocated memory.\n");
 }

 free(ptrMemBlock);

 return 0;
}

Note that the free() function returns a previously allocated block of memory to the free list.

To reiterate, size_t is a typedef representing the number of bytes to allocate.

The malloc() function returns a null pointer if it fails.

When allocating a block of memory of a particular type, we can use the sizeof() operator to calculate the number of bytes per unit to allocate. The use of sizeof() is important for maintaining portability.

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

int main()
{
 size_t byteSize = 512;
 //wchar_t technically isn't standard (?)
 //but we don't follow *their* rules here
 wchar_t *wszStr;

 wszStr = (wchar_t*)malloc(sizeof(wchar_t) * byteSize);

 if (wszStr == NULL) {
 printf("Failed to allocate buffer. Nuts!\n");
 exit(EXIT_FAILURE);
 }
 else {
 //copy the unicode string 
 //wcspy_s takes three params
 //the destination, the max number of wide 
 //chars to copy, and the source
 wcscpy_s(wszStr, byteSize, L"It can be said that the history of science is a history of the expansion of the human body's functionality, in other words, the history of humanity's cyberization.");
 }

 printf("%ls.\n", wszStr);

 free(wszStr);

 return 0;
}

Technically, it’s a good idea to initialize pointers to NULL at declaration and after free. With small programs such as these examples, it may seem extraneous, but its better to get in the habit of doing it always, even when unnecessary, then to forget even once when it is necessary.

Setting unused pointers to NULL is basically all-around good defensive programming, as it protects us from dangling pointer bugs, which can be difficult to track down. Always remember that in C and C++ pointers are inherently unsafe.

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

int main()
{
	int i;
	int *iPtrBlock = NULL;
	
	iPtrBlock = (int *)malloc(sizeof(int) * 4);

	if (iPtrBlock == NULL) {
		printf("Error allocating memory.\n");
		exit(EXIT_FAILURE);
	}

	*iPtrBlock = 42;
	*(iPtrBlock + 1) = 73;
	*(iPtrBlock + 2) = 1138;
	*(iPtrBlock + 3) = 404;

	for (i = 0; i < 4; i++) {
		printf("%d\n", *(iPtrBlock + i));
	}

	//verify we are not passing free()
	//a NULL pointer
	if (iPtrBlock != NULL) {
		free(iPtrBlock);
	}
	
	iPtrBlock = NULL;
	
    return 0;
}


The realloc() function will change the size of the space allocated on the heap. If realloc() returns a NULL, it was unable to allocate any more free space on the heap; however, this is an unlikely event given memory sizes and the amount of data we happen to be working with. The realloc() function returns a pointer, as it may have had to move the memory block in order to fulfill the request to increase the block’s size.

#include <stdlib.h>
#include <Windows.h>

int main()
{
 double dNum = 0;
 const size_t szBuffer_Size = 256;
 const size_t numDigits = 16;
 double *dStorage = (double*)malloc(sizeof(double));
 char szBuffer[szBuffer_Size];
 //get handle for stdin
 HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
 DWORD dwBytesRead;
 int iLocation = 0;
 

 while (true) {
 printf("Please enter a number: ");
 ReadFile(hStdin, szBuffer, szBuffer_Size, &dwBytesRead, NULL);
 if (dwBytesRead > numDigits) {
 szBuffer[numDigits] = '\0';
 }
 else {
 szBuffer[dwBytesRead] = '\0';
 }
 //convert string to double
 dNum = atof(szBuffer);

 printf("Entering %f into database.\n", dNum);
 *(dStorage + iLocation) = dNum;
 
 printf("Continue entering numbers? (y/n) ");
 ReadFile(hStdin, szBuffer, szBuffer_Size, &dwBytesRead, NULL);
 *(szBuffer + 1) = '\0';
 if (*szBuffer == 'n' || *szBuffer == 'N') {
 printf("Thank you.\n");
 break;
 }

 iLocation++;

 //allocate more memory
 dStorage = (double*)realloc(dStorage, sizeof(double) * (iLocation + 1));
 } //end while loop

 printf("Numbers entered: \n");
 while (iLocation >= 0) {
 printf("%f \t", *(dStorage + iLocation));
 iLocation--;
 }

 return 0;
}

Reviewing Dynamic Memory Allocation in C

We can find four memory management functions in the stdlib.h header file: malloc(), realloc(), calloc(), and free(). The malloc() function allocates memory from the heap. The realloc() function reallocates memory relative to a previously allocated block of memory. The calloc() function is like malloc(), but zeros out the memory that is allocated from the heap. Finally, the free() function returns a block of memory to the heap.

The malloc() function allocates a block of memory from the heap; if no memory is available, NULL is returned. Since there is no guarantee that a malloc() call will work, we should always check to see if the function returned NULL.

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

int main(void){
    
    void *memPtr = (void*)malloc(8);

    if(memPtr==NULL){
        printf("Could not allocate memory.\n");
    } else {
        printf("8 bytes allocated.\n");
    }

    free(memPtr);

    return 0;

}

The free() function accepts a pointer as an argument and returns the block of memory pointed to back to the heap.

In the next program I use bitwise operators to turn on bits in the memory block. While using bitwise operators is lots of fun, at least I think so, it isn’t the most necessary thing to know how to do. The most important thing to remember when using bitwise operators is that 0 and 1 have the same value in binary as they do in the base 10 number system. As a recap, (1 << n) will turn on the bit at position n, and the | operator basically ‘adds’ the number on the left to the  number on the right. If you’re interested in learning more, take a look at my other posts at https://aljensencprogramming.wordpress.com/tag/bitwise-operators/

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

int main(void){

    void *ptr = malloc(1);

    if(ptr!=NULL){
        //turn off all bits in the memory block
        *(char*)ptr = *(char*)ptr & 255;
        printf("%d\n", *(char*)ptr);
        
        //turn on first bit
        *(char*)ptr = *(char*)ptr | 1;
        printf("%d\n", *(char*)ptr);

        //turn on the fourth bit
        *(char*)ptr = *(char*)ptr | (1<<4);
        printf("%d\n", *(char*)ptr);
    }


    //free up the memory
    free(ptr);

    
    

    return 0;

}

Note that it isn’t necessary to explicitly cast the return value of the malloc() function; whether to cast or not to cast is a minor debate topic.

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


int main(void){


    int *ptr = malloc(4);
    int *ptr2 = (int*)malloc(4);

    *ptr = 404;
    *ptr2 = 1138;
    
    if(ptr!=NULL){
        printf("%d\n", *ptr);    
    }

    if(ptr2!=NULL){
        printf("%d\n", *ptr);
    }

    return 0;

}

When using malloc() its important to use the function to allocate the correct number of bytes. So far, we have been using magic numbers to specify the number of bytes to allocate, which is generally a bad idea. Standard procedures is to use the sizeof operator when specifying the number of bytes to allocate.

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

int main(void){

    int *iPtr = (int*)malloc(sizeof(int));

    double *dPtr = (double*)malloc(sizeof(double));


    *iPtr = 3000;
    *dPtr = 255.255;

    printf("%d \t %f\n", *iPtr, *dPtr);
    
    free(iPtr);
    free(dPtr);

    return 0;

}

Sometimes we need to increase or decrease the amount of memory allocated to a pointer, which can be done using the realloc() function. The realloc() function takes two arguments, the first argument is a pointer to the original block of memory, and the second is the new size, which can be larger or smaller than the size of the original memory block.

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

int main(void){

    int *intPtr = (int*)malloc(5*sizeof(int));
    
    *intPtr = 73;
    *(intPtr+1) = 47;
    *(intPtr+2) = 1138;
    *(intPtr+3) = 80;


    printf("%d\n", *(intPtr+1));
    printf("%d\n", *(intPtr+2));

    //resize the array
    intPtr = realloc(intPtr, 4*sizeof(int));
    
    printf("%d\n", *(intPtr+3));
    
free(intPtr);

    return 0;

}

If the size of the new memory block is smaller than the current block, the memory block remains in the same location. If the memory block is larger, either a new block in a new location will be allocated, and the contents of the old block copied over, or else if there is free consecutive memory next to the current block it will be expanded to include it.

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

int main(void){

    int *ints = (int*)malloc(10*sizeof(int));

    printf("ints is located at %p\n", ints);
    
    double *dubs = (double*)malloc(5*sizeof(double));
    
    //ints will most likely be put into a new location
    ints = realloc(ints, 20*sizeof(int));
    printf("ints is located at %p\n", ints);
    
    //memory location will remain the same
    ints = realloc(ints, 5*sizeof(int));
    printf("ints is located at %p\n", ints);

    //don't forget to free the allocated memory
    free(dubs);
    free(ints);
    
    return 0;

}

If you’re interested in learning more about C, take a look at http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/