【问题标题】:Redefining function from standard library从标准库重新定义函数
【发布时间】:2018-06-07 12:52:45
【问题描述】:

上下文:在最近的一次对话中,问题是“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 的警告(更准确地说,与问题无关:他们仍然警告我的strlen 定义中未使用参数s)。

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

【问题讨论】:

  • 如果你用gcc -Wall -Wstrict-prototypes -Werror 编译上面的代码,它会给出类似函数声明不是原型这样的错误,所以我想你提到的一点是当标头声明不包含时,它允许在 C 中重新定义标准函数吗?
  • @achal 哦,我不知道-Wstrict-prototypes,谢谢,我学到了一些东西。但是,如果我使用void 作为fmain 的参数来删除这些警告,程序的行为不会改变(它仍然打印4 10),所以我不确定我是否理解您评论的最后一部分。
  • 我猜strlen("abcd")显然是一个常量,编译器已将其替换为值 4,因为它可以识别 strlen()。 IOTH g("abcd") 不是 显然 常量,因此编译器会发出调用您的 strlen() 函数的代码(但尝试将 g() 和 strlen() 声明为静态内联)。
  • 出于险恶目的重用标准库函数名会调用未定义的行为。

标签: c gcc compilation clang


【解决方案1】:

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

以下任何子条款中具有外部链接的所有标识符......始终保留用作具有外部链接的标识符。

如果程序在保留标识符的上下文中声明或定义标识符(7.1.4 允许的除外),或将保留标识符定义为宏名称,则行为未定义。

“以下子条款”指定标准 C 库,包括 strlen。你的程序定义了strlen,所以它的行为是未定义的。

您观察到的情况是:

  • 无论您的定义如何,编译器都知道strlen 的行为方式,因此,在优化f 中的strlen("abcd") 时,它会在编译时评估strlen,结果为四个。
  • g("abcd")中,编译器无法识别,因为g的定义,这等价于strlen("abcd"),所以在编译时没有对其进行优化。相反,它将它编译为对g 的调用,并将g 编译为调用strlen,它还编译您对strlen 的定义,结果g("abcd") 调用g,它调用你的strlen,返回十个。

C 标准将允许编译器完全放弃您对strlen 的定义,因此g 返回四个。然而,一个好的编译器应该警告你的程序定义了一个保留的标识符。

【讨论】:

  • 有没有办法用4 替换strlen("abcd"); 删除源代码中函数调用产生的所需序列点?
  • @AndrewHenle:编译器负责确保在优化时语义保持正确。在这种情况下,由 6.5.2.2 10 指定的序列点仅保证在 (a) 函数指示符和参数的评估和 (b) 实际调用之间。这些都没有副作用,因此序列点无关紧要。参数和函数调用是否是相对于其他事物的序列,不受该序列点的控制。
猜你喜欢
  • 2022-10-14
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 1970-01-01
  • 2014-03-05
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
相关资源
最近更新 更多