In my code I use logging statements in order to better see what's going on. Sometimes I write code like the following:
int i = 1337;
// More stuff...
logger->info("i has the following value: " i);
When compiled and executed in debug mode this does not print out i
as expected (this is how it would work in Java/C# for example), it rather prints something garbled. In release mode however this might as well crash the entire application. What does the C
standard say about appending ints to a std::string
like I'm doing here?
Why does the compiler not warn me at all when I compile code invoking obvious undefined behavior like this? Am I missing something? I'm using Visual Studio 2022 (MSVC). The correct way to do the logging statement would be converting the int to a std::string
explicitly:
logger->info("i has the following value: " std::to_string(i));
However this bug easily slips through during development. My warning level is set to Level4 (/W4)
.
CodePudding user response:
The problem is that in
logger->info("i has the following value: " i);
you are not working with std::string. You are adding a string literal, i.e. a const char *
, and an int
. What this does it advance the pointer by 1337, which is way beyond the end of the string literal and therefore undefined behavior.
You should get a better compiler:
foo.cc:7:42: warning: offset ‘1337’ outside bounds of constant string [-Warray-bounds]
7 | foo("i has the following value: " i);
| ^
You can use a std::string literal like this:
#include <string>
using namespace std::literals;
void foo(std::string);
void bla() {
int i = 1337;
foo("i has the following value: "s i);
}
and then you get a "nice" error that "std::string int" isn't a thing in c :
foo.cc:8:40: error: no match for ‘operator ’ (operand types are ‘std::__cxx11::basic_string<char>’ and ‘int’)
8 | foo("i has the following value: "s i);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~
| | |
| std::__cxx11::basic_string<char> int
...
going on for 147 lines
After this it should be obvious that what you want is:
logger->info("i has the following value: "s std::to_string(i));
Using std::string literals avoids mistakes like this because it turns warnings (which your compiler doesn't even give) into hard errors forcing you to write correct code. So I recommend using the s
suffix for all strings.
CodePudding user response:
This line is correct,
logger->info("i has the following value: " i);
in the expression
"i has the following value: " i
there is used the pointer arithmetic.
For example if you will write
logger->info("i has the following value: " 6);
then this line has the same effect if to write
logger->info("the following value: ");
That is this line
logger->info("i has the following value: " i);
is equivalent to the line
logger->info( &"i has the following value: "[i]);
What does the C standard say about appending ints to a std::string like I'm doing here
In the expression there is no object of the type std::string
. There is used a string literal that has just an ordinary array type that is an operand of an expression with the pointer arithmetic. In the expression the string literal is implicitly converted to a pointer to its first element of the type const char *
.