【问题标题】:Weird behavior with a manually defined strlen手动定义的 strlen 的奇怪行为
【发布时间】:2017-01-19 16:16:04
【问题描述】:

一不小心,写了如下有趣的sn-p:

#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 函数是否被视为未定义行为?

【问题讨论】:

  • 参见 [reserved.names] 和 [depr.c.headers]。
  • 在我的机器上你的示例段错误。 (linux,gcc-version 6.2.1 20160830)我没想到。我希望代码打印“hello”并以代码 0 退出操作系统。
  • 我的示例也出现了段错误(gcc 5.4)。我建议在全局命名空间中提供strlen 函数是极其不寻常和“冒险”的,因为如果您首先包含&lt;cstring&gt;,这将覆盖libc 版本(带有“C”链接),因为那很自然也覆盖std::strlen,它可以在库的其他地方使用。例如。 std::cout 似乎产生了对 strlen 的调用。
  • 你的代码里有ub

标签: c++ std language-lawyer undefined-behavior


【解决方案1】:

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

这是来自cstring的相关摘录:

namespace std _GLIBCXX_VISIBILITY(default)
{
 _GLIBCXX_BEGIN_NAMESPACE_VERSION

  using ::strlen;
  ...

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

【讨论】:

  • 您能解释一下删除return 时的行为吗?调用是否刚刚优化(我认为 gcc 可以这样做,因为它是标准库函数)?
  • @Kevin,是的,但我会在答案中详细说明。
  • 这不是函数重载。 std::strlen 有参数(char const *);一个函数不能用相同的参数列表重载。
  • @M.M,这是一个公平的观点。然而它的行为就好像它会超载,你不同意吗?
【解决方案2】:

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

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

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

如果程序在保留名称的上下文中声明或定义名称,除非本条款明确允许,否则其行为未定义。

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

【讨论】:

    【解决方案3】:

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

    有时你的 strlen 被调用,有时标准的 strlen 被调用的原因可能与 strlen 的许多实现是宏而不是函数的事实有关。它甚至可以在汇编器中实现。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-24
      • 2014-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-11
      相关资源
      最近更新 更多