上下文:在最近的一次谈话中,问题“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
。我尝试使用 gcc
和 clang
进行编译,并使用各种标志(-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/