Home > front end >  Why is appending an int to a std::string undefined behavior with no compiler warning in C ?
Why is appending an int to a std::string undefined behavior with no compiler warning in C ?

Time:05-09

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 *.

  • Related