从标准库重新定义函数 [英] Redefining function from standard library
问题描述
上下文:在最近的对话中,问题gcc/clang 在编译时执行 strlen("static string")
吗?上来了.经过一些测试,无论优化水平如何,答案似乎都是肯定的.看到这甚至在 -O0
完成我有点惊讶,所以我做了一些测试,最终得到了以下代码:
Context: In a recent conversation, the question "does gcc/clang do strlen("static string")
at compile time?" came up. After some testing, the answer seems to be yes, regardless the level of optimization. I was a bit surprised to see this done even at -O0
, so I did some testing, and eventually arrived to the following code:
#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
",f(),g("abcd"));
return 0;
}
令我惊讶的是,它打印的是 4 10
而不是 10 10
.我尝试使用 gcc
和 clang
以及各种标志(-pedantic
、-O0
、-O3
, -std=c89
, -std=c11
, ...) 并且测试之间的行为是一致的.
To my surprise, it prints 4 10
and not 10 10
. I tried compiling with gcc
and clang
, and with various flags (-pedantic
, -O0
, -O3
, -std=c89
, -std=c11
, ...) and the behavior is consistent between the tests.
因为我没有包含 string.h
,所以我希望使用我对 strlen
的定义.但是汇编代码确实显示strlen("abcd")
基本上被return 4
替换了(这是我在运行程序时观察到的).
Since I didn't include string.h
, I expected my definition of strlen
to be used. But the assembly code shows indeed that strlen("abcd")
was basically replaced by return 4
(which is what I'm observing when running the program).
此外,编译器不会打印带有 -Wall -Wextra
的警告(更准确地说,与问题无关:他们仍然警告参数 s
在我的定义中未使用strlen
).
Also, the compilers print no warnings with -Wall -Wextra
(more precisely, none related to the issue: they still warn that parameter s
is unused in my definition of strlen
).
出现两个(相关)问题(我认为它们足够相关,可以在同一问题中提出):
- 是否允许在声明不包含标准函数的头文件中重新定义 C 中的标准函数?
- 该程序是否按其应有的方式运行?如果是这样,究竟会发生什么?
Two (related) questions arise (I think they are related enough to be asked in the same question):
- is it allowed to redefine a standard function in C when the header declaring it isn't included?
- does this program behave as it should? If so, what happens exactly?
推荐答案
Per C 2011(草案 N1570)7.1.3 1 和 2:
Per C 2011 (draft N1570) 7.1.3 1 and 2:
以下任何子条款中具有外部链接的所有标识符……始终保留用作具有外部链接的标识符.
All identifiers with external linkage in any of the following subclauses … are always reserved for use as identifiers with external linkage.
如果程序在保留标识符的上下文中声明或定义标识符(7.1.4 允许的情况除外),或将保留标识符定义为宏名称,则行为未定义.
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
,所以它的行为是未定义的.
The "following subclauses" specify the standard C library, including strlen
. Your program defines strlen
, so its behavior is undefined.
你观察到的情况是:
- 编译器知道
strlen
应该如何表现,而不管您的定义如何,因此,在优化f
中的strlen("abcd")
时code>,它在编译时计算strlen
,结果为 4. - 在
g("abcd")
中,编译器无法识别,因为g
的定义,这相当于strlen(";abcd")
,因此它不会在编译时对其进行优化.相反,它将它编译为对g
的调用,并将g
编译为调用strlen
,它还编译您对的定义strlen
,结果g("abcd")
调用g
,它调用你的strlen
,它返回 10.
- The compiler knows how
strlen
is supposed to behave, regardless of your definition, so, while optimizingstrlen("abcd")
inf
, it evaluatesstrlen
at compile time, resulting in four. - In
g("abcd")
, the compiler fails to recognize that, because of the definition ofg
, this is equivalent tostrlen("abcd")
, so it does not optimize it at compile time. Instead, it compiles it to a call tog
, and it compilesg
to callstrlen
, and it also compiles your definition ofstrlen
, with the result thatg("abcd")
callsg
, which calls yourstrlen
, which returns ten.
C 标准将允许编译器完全丢弃您对 strlen
的定义,因此 g
返回四个.然而,一个好的编译器应该警告你的程序定义了一个保留标识符.
The C standard would allow the compiler to discard your definition of strlen
completely, so that g
returned four. However, a good compiler should warn that your program defines a reserved identifier.
这篇关于从标准库重新定义函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!