Home > Back-end >  Warning: null destination pointer [-Wformat-overflow=] with GCC 11.2.1
Warning: null destination pointer [-Wformat-overflow=] with GCC 11.2.1

Time:07-17

Here is my code:

#include <iostream>
#include <cstdio>

int main()
{
    char *str = new char[64] ;
    std::sprintf(str, "msg: %s", "hello world") ;

    std::cout << str << std::endl ;
    delete [] str ;

    return 0 ;
}

With GCC 11.2.1, using the following command:

g   -O -fsanitize=undefined -Wformat-overflow test.cpp

I get:

test.cpp:7:17: warning: null destination pointer [-Wformat-overflow=]
    7 |     std::sprintf(str, "msg: %s", "hello world") ;
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I failed to understand the reason for the warning. Did I do anything wrong?

CodePudding user response:

This seems like a bug/false-positive warning from the g compiler. The message is trying to warn you that using a pointer variable as the destination for the sprintf function could fail if that pointer is null (or points to a buffer of insufficient size).

It is 'trivial' to suppress this warning: simply add a check that str is not null before calling sprintf:

if (str) std::sprintf(str, "msg: %s", "hello world");

However, your code is fine as it stands, and that check is entirely superfluous, because the standard operator new [], as you have used it, cannot return a null pointer. You are using "Version (2)" of new as described on this cppreference page. Note, from that same page:

Return value

1-4) non-null pointer to suitably aligned memory of size at least size

If your new char[64] expression fails to allocate sufficient memory, then an exception will be thrown and (as your code stands) the sprintf function will not be called.

CodePudding user response:

To add to Adrian's answer while you code is correct the reason GCC is giving the warning is due to the explicit call to delete[]. I found that commenting out this line removed the warning. I think GCC is seeing the delete[] call in a bare-bones context (maybe because it is outside a destructor) and it thinks the memory might be freed while in use.

The warning persists if the code is moved to a purely C context (using malloc and free) with the gcc command, meaning it might be detecting frees (using either free() or delete[]) and warning you that data might be read/written after free (even though in this case its not). Could be a error with GCC not removing the copy it has of the char arrays pointer but I'm not too sure. There might be a GCC bug report on it but I couldn't find it. Here's the code I tested.

Note: GCC version: 11.3.0, minor increment but still persistent to GCC-11. I still used the same flags/options as your example.

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

int main()
{
    char* str = (char*)malloc(sizeof(char) * 64);

    sprintf(str, "msg: %s", "hello world");
    
    printf("%s\n", str);

    free(str);

    return 0;
}

When using g specifically though, if you use malloc and free it removes the warning for some reason. Here's the corresponding code:

#include <iostream>
#include <cstdio>

int main()
{
    char* str = (char*)std::malloc(sizeof(char) * 64);

    std::sprintf(str, "msg: %s", "hello world");
    
    std::cout << str << std::endl;

    std::free(str);

    return 0;
}

Note: I used std::cout for consistency with your given code and because the warning persists if I used std::printf.

If you're using C specifically, you can use std::string.

#include <iostream>
#include <string>
#include <cstdio>

int main()
{
    std::string str(64, ' ');  ///< allocate a std::string with 64 characters, each initialised to a space (' ').

    std::sprintf(&str[0], "msg: %s", "hello world");  ///< Get the pointer to the first value (C-style) by indexing the string and using the address operator (&)
    
    std::cout << str << std::endl;

    return 0;
}

The code above has no manual memory management, its done by std::string. Although I do get it if you're learning about the memory facilities in C . Hopefully that's all clear.

Note: std::string's c_str() method wouldn't work here with std::sprintf because it expects a char* but c_str() returns a const char*.

Note: An yes, I know & can also be the reference operator or the bit-and operator but in this context it's used to obtain the address like in C.

  • Related