手动定义的 strlens 的奇怪行为

Weird behavior with a manually defined strlen

本文关键字:strlens 定义      更新时间:2023-10-16

偶然地,我写了以下有趣的片段:

#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调用计算字符串长度的 real 函数(并且没有打印任何内容)。这就是我期望看到的。

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

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

以下是cstring的相关摘录:

namespace std _GLIBCXX_VISIBILITY(default)
{
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
  using ::strlen;
  ...

当你删除 return 语句时,GCC 会完全优化调用,因为它知道strlen是没有副作用的函数,它实际上是一个保留名称,不应该被重载。我假设编译器可能会在这里给你一个警告,但唉,它没有,因为它不是必需的。

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

使用外部链接声明的标准 C 库中的每个名称都保留给实现,以便在命名空间 std 和全局命名空间中用作具有外部"C"链接的名称。

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

如果程序在保留它的上下文中声明或定义名称,则此子句明确允许的名称除外,则其行为是未定义的。

因此,您的程序具有未定义的行为。

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

为什么有时调用 strlen,有时调用标准 strlen,可能

与 strlen 的许多实现是宏而不是函数有关。它甚至可以在汇编程序中实现。

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