Initializing a Buffer – Your Own memset() Function

As I wrote in last week’s Lesson, the C23 standard renders the memset() function — a standard C library function for over 30 years — obsolete. If you want to initialize a buffer, you can use the secure memset_s() or just initialize the buffer manually or with your own memset()-like function.

Alas, the parade of safe C functions isn’t widely supported. Each of these functions shares a name with their “unsafe” (or easily abused) counterparts, but with the _s suffix. This rule is true for memset(), where the safe version is named memset_s(). Because these safe functions are inconsistently implemented, a definitive method to detect their inclusion with any compiler isn’t available. So if you want to use a safer version of memset(), and memset_s() isn’t available to your compiler, you must code your own or just manually initialize a buffer.

The following code zeroes out the human structure variable my:

2022_03_19-Lesson-a.c

#include <stdio.h>

int main()
{
    struct human {
        char name[32];
        int age;
        float weight;
    } my;
    int x;

    for( x=0; x<32; x++)
        my.name[x] = '\0';
    my.age = 0;
    my.weight = 0.0;

    return(0);
}

The for loop at Line 12 initializes all 32 elements of the name array member to the null character, \0. Lines 14 and 15 set the other two structure members to zero. This process effectively initializes the buffer, removing whatever garbage already exists in memory.

The program has no output, though I ran it through the Code::Blocks debugger to watch as the relevant memory chunk was cleared. Figure 1 illustrates the before (above) and after (below) effect of clearing the structure’s memory.

Figure 1. A memory dump from the Code::Blocks debugger, showing before (above) and after( below).

The problem with this approach is that it’s specific to one type of structure. A better approach is to zero out memory as a buffer — the entire thing — regardless of its specific data type contents. This approach is used in the following code update:

2022_03_19-Lesson-b.c

#include <stdio.h>

int main()
{
    struct human {
        char name[32];
        int age;
        float weight;
    } my;
    int x;
    char *b;

    b = (char *)&my;
    for( x=0; x<sizeof(struct human); x++)
        *(b+x) = 0;

    return(0);
}

This code adds char pointer b. Its value is set to the address of human struct variable my at Line 13, which is typecast as a char pointer. Remember that in C, a char is sorta kinda the same as a byte. The for loop then initializes the entire structure’s contents to zero, using the sizeof operator to return the structure’s size in memory.

Of course, it would be better to put the zeroing code into a function, as is done here:

2022_03_19-Lesson-c.c

#include <stdio.h>

void memzero(void *b, int len)
{
    int x;
    char *a;

    a = (char *)b;
    for( x=0; x<len; x++)
        *(a+x) = 0;
}

int main()
{
    struct human {
        char name[32];
        int age;
        float weight;
    } my;

    memzero(&my,sizeof(struct human));

    return(0);
}

The memzero() function has a void pointer as its first argument and the buffer size as its second. The void data type allows any pointer to be passed, such as the address of human structure my at Line 21. The structure, not being a pointer itself, is prefixed with the &, address-of operator.

Within the memzero() function, the void pointer b is assigned to char pointer a for processing. The result is the same: The buffer is initialized to zero. So this function works as a replacement for memset(), but it’s still subject to compiler optimization.

To disable compiler optimization, use the command line switch -O0 (capital O, zero). This switch is the default, so it may not have any effect on your code.

A better approach is to classify int variable x as volatile at Line 5:

volatile int x;

The volatile qualifier directs the compiler not to mess with variable x, which should ensure that the loop is processed completely and not optimized in any way. Better than using this trick is to find a compiler that supports the memset_s() function, or just wait until the C23 release becomes standard.

Leave a Reply