手动定义的strlen的奇怪行为

时间:2017-01-19 16:16:05

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

无意中,我写了以下有趣的片段:

#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函数定义为未定义的行为?

3 个答案:

答案 0 :(得分:12)

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

以下是namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION using ::strlen; ... 的相关摘录:

strlen

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

答案 1 :(得分:4)

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

  

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

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

  

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

所以你的程序有不确定的行为。

答案 2 :(得分:0)

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

有时调用strlen并且有时调用标准strlen的原因可能与strlen的许多实现是宏而不是函数有关。它甚至可以在汇编程序中实现。

如果是宏,则会运行标准宏。 此外,如果删除return,优化程序可以删除函数调用。你可以比较-O0。