为什么 std::strlen() 在不终止空字符的情况下处理 char 数组?这是编译器优化吗?

Why does std::strlen() work on char arrays WITHOUT terminating null characters? Is this a compiler optimization?

我读过的所有文章都说将非 null 终止的 char 数组传递给 std::strlen 是未定义的行为,可能会导致程序崩溃。然而,下面的代码(在 Cygwin 上用 g++ 编译)工作得很好。

这是怎么回事?

char test_cases[4][80] = {{'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!'}, {}, {'1'}, {'A', 'B', 'C'}};
size_t num_test_cases = std::size(test_cases); // C++17

for (size_t i = 0; i < num_test_cases; ++i) 
{
    std::cout << std::strlen(test_cases[i]) << std::endl;
}

输出:

13
0
1
3

says that passing a non null-terminated char array to std::strlen is undefined behavior

正确。

However, the code below works just fine.

所有字符串都是空终止的,因此没有未定义的行为。

无论如何,您不能假设具有未定义行为的程序不会出现在 "work just fine" 中。这没有什么不寻常的。

and will likely cause the program to crash.

期望 "likely cause the program to crash" 有未定义的行为不是一个好主意。 UB完全有可能不导致程序崩溃

数组的构造意味着任何未使用的插槽都可以方便地设置为零大小。

所以你写的是完全合法和一致的。

您是否为 "Hello, world!"

定义了正确的缓冲区大小
char test_cases[4][13]

你会得到"broken"答案,并触及UB的边缘。

另外,因为您将其声明为第一个缓冲区,它会 运行 进入第二个缓冲区,因此会给出错误答案而不是一些致命错误。

其实,再看一遍,因为你把第二个字符串定义为空,你仍然不会看到错误,因为溢出数据的第一个字节可能也是零填充的!

我说也许,因为没有值的 {} 实际上不是合法的 C。它是合法的 C++11,但我不完全确定行为是否是为了确保所有成员都归零,如果 C+ +11 聚合 "style" 初始化程序被调用。事实上,由于您的输出,{} 必须完成 "right" 事情。

通常内存中有太多的零,你的字符串通常最终会被终止!正如@John 提到的,这是外星人从您的银行账户中窃取资金的机会。

您的情况是“零初始化”的常见情况。 It's perfectly defined.

Initialization from brace-enclosed lists

When an array is initialized with a brace-enclosed list of initializers, the first initializer in the list initializes the array element at index zero (unless a designator is specified) (since C99), and each subsequent initializer without a designator (since C99)initializes the array element at index one greater than the one initialized by the previous initializer.

因为你分配了超过13个字符(80),所以其他的都用'[=11=]'(值为0的字符)填充。所以 strlen 完全按照预期工作,因为您的空间比您预期的要多。

来自 cppreference 的其他示例完全符合您的情况:

int x[] = {1,2,3}; // x has type int[3] and holds 1,2,3
int y[5] = {1,2,3}; // y has type int[5] and holds 1,2,3,0,0
int z[3] = {0}; // z has type int[3] and holds all zeroes