手动定义的 strlen 的奇怪行为

Weird behavior with a manually defined strlen

一不小心,我写了以下有趣的片段:

#include <iostream>
#include <cstring>

size_t strlen(const char* str) {
    std::cout << "hello";
    return 0;
}

int main() {
    return std::strlen("sdf");
}

出乎我的意料,在GCC 5.1中输出是"hello",这意味着我的strlen正在被调用。更有趣的是,如果我删除 return,即仅用 std::strlen("sdf"); 调用替换 main,则不会打印任何内容!

我还尝试了 Clang,std::strlen 调用了计算字符串长度的实际函数(没有打印任何内容)。这就是我期望看到的。

这怎么解释?定义我自己的 strlen 函数是否被视为未定义行为?

这里没有什么有趣的东西,只是一个函数重载和一些未定义的行为。您使用自己的版本重载了库函数 strlen()。由于在 GCC 中 std::strlen 的实现只不过是命名空间 std 内的库函数调用,所以您会得到所看到的结果。

这里是来自cstring的相关摘录:

namespace std _GLIBCXX_VISIBILITY(default)
{
 _GLIBCXX_BEGIN_NAMESPACE_VERSION

  using ::strlen;
  ...

并且当您删除 return 语句时,GCC 会完全优化调用,因为它知道 strlen 是没有 side-effects 的函数,它实际上是一个保留名称,不应超载。我想,编译器可能会在这里给你一个警告,但是,唉,它没有,因为它不是必需的。

您在默认的 std 命名空间中定义了 strlen,从而覆盖了标准命名空间。

之所以有时会调用您的strlen,有时会调用标准的strlen,可能与这样一个事实有关,即许多strlen 的实现是宏而不是函数。它甚至可以在汇编程序中实现。

如果是宏,则标准宏会 运行。 此外,如果您删除 return,优化器可以删除函数调用。您可以与 -O0.

进行比较

根据 C++14 [extern.names]/3,::strlen 被保留:

Each name from the Standard C library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace.

以及使用保留名称 [reserved.names]/2 的效果:

If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this Clause, its behavior is undefined.

所以你的程序有未定义的行为。