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.