从标准库重新定义功能 [英] 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\n",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
).
出现两个(相关)问题(我认为它们之间的相关性足以在同一个问题中提出):
-是否可以重新定义
-此程序的行为是否应做?如果是这样,究竟发生了什么?
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?
推荐答案
每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.
在您观察到的情况下,发生的事情是:
What is happening in the case you observe is:
- 编译器知道
strlen
的行为方式,而不管您的定义如何,因此在优化$
,在编译时评估f
中的strlen( abcd)strlen
,结果为四个。 - 在
g( abcd)
中,由于g
,它等效于strlen( abcd)
,因此它不会在编译时对其进行优化。相反,它将其编译为对g
的调用,并且将其编译为g
来调用strlen
,它还会编译您的strlen
定义,结果是g( abcd)
调用g
,这将调用您的strlen
,并返回十。
- 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屋!