arrayscsecuritybuffersizeof

Sizeof vs array size


I've got a question regarding the sizeof() operator. Browsing Open Source projects written in C obviously, some developers tend to use the sizeof() operator in one place and on other places the literal array size.

For example, there is code written like this:

...
char   buf[2048];

memset(buf, 0, sizeof(buf));

... = snprintf(buf, 2048, ...);

...

The developer used sizeof(buf) for memset() but referenced the literal buffer size 2048 directly to snprintf() instead of sticking to sizeof(buf). Is this only a bad habit/inconsistency in writing code or was/is the use of sizeof() inappropriate for functions such as snprintf()?

I see this often and really wondered, why not always use sizeof() in such cases? Wouldn't this ease the maintenance of code? Imagine the snprintf() call was somewhere placed much more lower within the function body, a developer changes the size of the buffer on top of the function to e.g. 1024, memset() would catch the change but snprintf() could cause a overflow.


Solution

  • Hard coding the array size or length is indeed a risky habit as the code may become incorrect if the array length is changed to a lesser value and the code is not updated consistently.

    Using sizeof(buf) or countof(buf) defined as

    #define countof(a) (sizeof(a)/sizeof(*(a)))
    

    is recommended, but may also be incorrect if buf is not defined as an array in the scope of the function call. If buf is a pointer, received as a function argument, sizeof(buf) will be the size of a pointer, not that of the array it points to. What makes it especially confusing is the case of function arguments specified using the array syntax:

    // !!! This definition is equivalent to void initialize_array(char *buf)
    void initialize_array(char buf[1024]) {
        // BUG! sizeof(buf) is sizeof(char *), much less than 1024
        memset(buf, 0, sizeof(buf));
    }
    

    The C Standard does not provide a simple way to evaluate an array length only for actual arrays, so I would recommend this approach:

        ...
        char buf[2048];
        const size_t buf_size = sizeof(buf); // always next to the array definition
        
        memset(buf, 0, buf_size);
        
        ... = snprintf(buf, buf_size, ...);
        
        ...
    

    The compiler will eliminate the buf_size variable and generate optimal code. Should you receive buf as an argument, this idiom will ensure you also get the buffer size or length as an additional argument.

    For types larger than char, passing the array length is preferable and naming that buf_length or buf_count is much safer:

        ...
        int buf[2048];
        const size_t buf_count = sizeof(buf) / sizeof(*buf);
        
        memset(buf, 0, buf_count * sizeof(*buf));
        
        for (size_t i = 0; i < buf_count; i++) {
            if (handle_case(i))
                bur[i] = 1;
        }
        ...