为什么在 C++ 中没有编译器警告的情况下将 int 附加到 std::string 未定义行为?
Why is appending an int to a std::string undefined behavior with no compiler warning in C++?
在我的代码中,我使用 logging statements 以便更好地了解发生了什么。有时我会写如下代码:
int i = 1337;
// More stuff...
logger->info("i has the following value: " + i);
在调试模式下编译和执行时,它不会按预期打印出 i
(例如,这就是它在 Java/C# 中的工作方式),而是打印出一些乱码。然而,在发布模式下,这可能会使整个应用程序崩溃。 C++
标准对像我在这里做的那样将整数附加到 std::string
有什么看法?
为什么当我编译调用这样明显未定义行为的代码时,编译器根本不警告我?我错过了什么吗?我正在使用 Visual Studio 2022 (MSVC)。执行日志记录语句的正确方法是将 int 显式转换为 std::string
:
logger->info("i has the following value: " + std::to_string(i));
但是这个错误很容易在开发过程中被忽略。我的警告级别设置为 Level4 (/W4)
.
这一行是正确的,
logger->info("i has the following value: " + i);
在表达式中
"i has the following value: " + i
这里用到了指针运算
例如,如果您要写
logger->info("i has the following value: " + 6);
那么这一行写成
也是一样的效果
logger->info("the following value: ");
也就是这条线
logger->info("i has the following value: " + i);
相当于行
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
表达式中没有 std::string
类型的对象。使用了一个字符串文字,它只有一个普通的数组类型,它是一个带有指针算法的表达式的操作数。在表达式中,字符串文字被隐式转换为指向其第一个 const char *
.
类型元素的指针
问题是在
logger->info("i has the following value: " + i);
您没有使用 std::string
。您正在将 int
添加到字符串文字,即 const char[]
数组。 const char[]
在某些情况下会衰减为 const char*
指针。在这种情况下,int
将该指针向前推进 1337 个字符,这远远超出了字符串文字的末尾,因此是未定义的行为。
你应该得到一个更好的编译器来警告你,即:
foo.cc:7:42: warning: offset ‘1337’ outside bounds of constant string [-Warray-bounds]
7 | foo("i has the following value: " + i);
| ^
您可以像这样使用 std::string
文字:
#include <string>
using namespace std::literals;
void foo(std::string);
void bla() {
int i = 1337;
foo("i has the following value: "s + i);
}
然后你得到一个“更好”的错误,“std::string + int”在 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
在这之后,应该很明显你想要的是这个:
logger->info("i has the following value: "s + std::to_string(i));
使用 std::string
文字可以避免这样的错误,因为它将警告(您的编译器甚至不会给出)变成硬错误,迫使您编写正确的代码。所以我建议对所有字符串使用 s
后缀。
在我的代码中,我使用 logging statements 以便更好地了解发生了什么。有时我会写如下代码:
int i = 1337;
// More stuff...
logger->info("i has the following value: " + i);
在调试模式下编译和执行时,它不会按预期打印出 i
(例如,这就是它在 Java/C# 中的工作方式),而是打印出一些乱码。然而,在发布模式下,这可能会使整个应用程序崩溃。 C++
标准对像我在这里做的那样将整数附加到 std::string
有什么看法?
为什么当我编译调用这样明显未定义行为的代码时,编译器根本不警告我?我错过了什么吗?我正在使用 Visual Studio 2022 (MSVC)。执行日志记录语句的正确方法是将 int 显式转换为 std::string
:
logger->info("i has the following value: " + std::to_string(i));
但是这个错误很容易在开发过程中被忽略。我的警告级别设置为 Level4 (/W4)
.
这一行是正确的,
logger->info("i has the following value: " + i);
在表达式中
"i has the following value: " + i
这里用到了指针运算
例如,如果您要写
logger->info("i has the following value: " + 6);
那么这一行写成
也是一样的效果logger->info("the following value: ");
也就是这条线
logger->info("i has the following value: " + i);
相当于行
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
表达式中没有 std::string
类型的对象。使用了一个字符串文字,它只有一个普通的数组类型,它是一个带有指针算法的表达式的操作数。在表达式中,字符串文字被隐式转换为指向其第一个 const char *
.
问题是在
logger->info("i has the following value: " + i);
您没有使用 std::string
。您正在将 int
添加到字符串文字,即 const char[]
数组。 const char[]
在某些情况下会衰减为 const char*
指针。在这种情况下,int
将该指针向前推进 1337 个字符,这远远超出了字符串文字的末尾,因此是未定义的行为。
你应该得到一个更好的编译器来警告你,即:
foo.cc:7:42: warning: offset ‘1337’ outside bounds of constant string [-Warray-bounds]
7 | foo("i has the following value: " + i);
| ^
您可以像这样使用 std::string
文字:
#include <string>
using namespace std::literals;
void foo(std::string);
void bla() {
int i = 1337;
foo("i has the following value: "s + i);
}
然后你得到一个“更好”的错误,“std::string + int”在 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
在这之后,应该很明显你想要的是这个:
logger->info("i has the following value: "s + std::to_string(i));
使用 std::string
文字可以避免这样的错误,因为它将警告(您的编译器甚至不会给出)变成硬错误,迫使您编写正确的代码。所以我建议对所有字符串使用 s
后缀。