c - 从标准库重新定义函数

标签 c gcc compilation clang

上下文:在最近的一次谈话中,问题“gcc/clang 在编译时执行 strlen("static string") 吗?”上来了。经过一些测试,答案似乎是肯定的,无论优化级别如何。甚至在 -O0 上看到这也完成了,我有点惊讶,所以我做了一些测试,最终得出了以下代码:

#include <stdio.h>

unsigned long strlen(const char* s) {
  return 10;
}

unsigned long f() {
  return strlen("abcd");
}

unsigned long g(const char* s) {
  return strlen(s);
}

int main() {
  printf("%ld %ld\n",f(),g("abcd"));
  return 0;
}

令我惊讶的是,它打印出 4 10 而不是 10 10。我尝试使用 gccclang 进行编译,并使用各种标志(-pedantic-O0 -O3, -std=c89, -std=c11, ...) 并且测试之间的行为是一致的。

因为我没有包含 string.h,所以我希望使用我对 strlen 的定义。但汇编代码确实显示 strlen("abcd") 基本上被 return 4 取代(这是我在运行程序时观察到的)。

此外,编译器不会使用 -Wall -Wextra 打印任何警告(更准确地说,与问题无关:他们仍然警告参数 s 在我的定义中未使用strlen).

出现了两个(相关)问题(我认为它们足够相关,可以在同一个问题中提出):
- 是否允许在 C 语言中重新定义标准函数,但不包含声明它的 header ?
- 这个程序的行为是否正常?如果是这样,具体会发生什么?

最佳答案

根据 C 2011(N1570 草案)7.1.3 1 和 2:

All identifiers with external linkage in any of the following subclauses … are always reserved for use as identifiers with external linkage.

If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.

“以下子条款”指定标准 C 库,包括 strlen。您的程序定义了 strlen,因此其行为未定义。

您观察到的情况是:

  • 无论您的定义如何,编译器都知道 strlen 的行为方式,因此,在优化 f 中的 strlen("abcd") 时>,它在编译时计算 strlen,结果是四个。
  • g("abcd") 中,编译器无法识别,因为 g 的定义,这等同于 strlen("abcd "),因此它不会在编译时对其进行优化。相反,它将其编译为对 g 的调用,并将 g 编译为调用 strlen,并且还会编译您对 的定义strlen,结果 g("abcd") 调用 g,后者调用您的 strlen,返回 10。

C 标准将允许编译器完全丢弃您对 strlen 的定义,以便 g 返回 4。但是,一个好的编译器应该警告您的程序定义了保留标识符。

关于c - 从标准库重新定义函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50741722/

相关文章:

c++ - 在 C++ 中对数组执行随机排列

c - 关于代码片段中分配内存的差异的问题?

c - 将代码拆分为函数后,它无法正常工作。 C语言编程

c++ - 将 main.cpp 更改为 main.c 并停止在 Clion 上使用 CMake 进行构建

c++ - 运行时发生奇怪的崩溃

将源代码从 ARMv5 编译到 ARMv6

c++ - 链接错误 : undefined references to stdscr and wgetch

c - 尝试添加字符串和字符时 strcat 不起作用?

c++ - 为什么调用不接受带参数的函数的函数在 C 中编译但在 C++ 中不编译

c++ - 解决链接器错误MySQL Connector/C++