为什么在 C++ 中将 int 附加到 std::string 未定义行为且没有编译器警告?

Bul*_*aza 1 c++ append undefined-behavior visual-studio visual-c++

在我的代码中,我使用日志语句以便更好地了解发生了什么。有时我会编写如下代码:

int i = 1337;
// More stuff...
logger->info("i has the following value: " + i);
Run Code Online (Sandbox Code Playgroud)

当在调试模式下编译和执行时,它不会i按预期打印出来(例如,这就是它在 Java/C# 中的工作方式),而是打印一些乱码。然而,在发布模式下,这可能会导致整个应用程序崩溃。C++标准对于将整数附加到std::string我在这里所做的事情有什么规定?

当我编译调用像这样的明显未定义行为的代码时,为什么编译器根本不警告我?我错过了什么吗?我正在使用 Visual Studio 2022 (MSVC)。执行日志记录语句的正确方法是将 intstd::string显式转换为 a:

logger->info("i has the following value: " + std::to_string(i));
Run Code Online (Sandbox Code Playgroud)

然而,这个错误在开发过程中很容易被忽视。我的警告级别设置为Level4 (/W4)。

Gos*_*low 11

问题是在

\n
logger->info("i has the following value: " + i);\n
Run Code Online (Sandbox Code Playgroud)\n

您没有与 一起工作std::string。您正在将 an 添加int到字符串文字,即const char[]数组。在某些情况下,该const char[]值会衰减为const char*指针。在这种情况下,int指针向前前进 1337 个字符,这远远超出了字符串文字的末尾,因此是未定义的行为。

\n

您应该获得一个更好的编译器来警告您这一点,即:

\n
foo.cc:7:42: warning: offset \xe2\x80\x981337\xe2\x80\x99 outside bounds of constant string [-Warray-bounds]\n    7 |     foo("i has the following value: " + i);\n      |                                          ^\n
Run Code Online (Sandbox Code Playgroud)\n
\n

您可以使用std::string如下文字:

\n
#include <string>\nusing namespace std::literals;\n\nvoid foo(std::string);\n\nvoid bla() {\n    int i = 1337;\n    foo("i has the following value: "s + i);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后你会得到一个“更好”的错误,即“std::string + int”不是 C++ 中的东西:

\n
foo.cc:8:40: error: no match for \xe2\x80\x98operator+\xe2\x80\x99 (operand types are \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 and \xe2\x80\x98int\xe2\x80\x99)\n    8 |     foo("i has the following value: "s + i);\n      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~\n      |         |                                |\n      |         std::__cxx11::basic_string<char> int\n...\ngoing on for 147 lines\n
Run Code Online (Sandbox Code Playgroud)\n

在此之后,很明显您想要的是:

\n
logger->info("i has the following value: "s + std::to_string(i));\n
Run Code Online (Sandbox Code Playgroud)\n

使用std::string文字可以避免这样的错误,因为它将警告(编译器甚至不会给出)变成硬错误,迫使您编写正确的代码。所以我建议使用s对所有字符串使用后缀。

\n

  • 很好的答案。`你应该得到一个更好的编译器`:哈哈,这是最新的MSVC编译器,我基本上需要使用Windows开发工具,因为我的软件只有Windows。至少 `gcc` 有这种类型的警告是件好事。希望 MSVC 也能尽快拥有它,因为这可能会令人沮丧。 (2认同)

Vla*_*cow 5

这条线是正确的,

logger->info("i has the following value: " + i);
Run Code Online (Sandbox Code Playgroud)

在表达式中

"i has the following value: " + i
Run Code Online (Sandbox Code Playgroud)

这里使用了指针运算。

例如,如果你会写

logger->info("i has the following value: " + 6);
Run Code Online (Sandbox Code Playgroud)

那么如果写这一行有相同的效果

logger->info("the following value: ");
Run Code Online (Sandbox Code Playgroud)

就是这一行

logger->info("i has the following value: " + i);
Run Code Online (Sandbox Code Playgroud)

相当于线

logger->info( &"i has the following value: "[i]);
Run Code Online (Sandbox Code Playgroud)

C++ 标准对于将 int 附加到 std::string 有何规定,就像我在这里所做的那样

表达式中没有 类型的对象std::string。使用的字符串文字只有一个普通的数组类型,它是带有指针算术的表达式的操作数。在表达式中,字符串文字隐式转换为指向其类型的第一个元素的指针const char *。