上下文:在最近的一次对话中,问题" 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
。< / p>
出现了两个(相关的)问题(我认为它们足够相关,可以在同一个问题中提出):
- 当标题声明不包括在内时,是否允许在C中重新定义标准函数?
- 这个程序的行为应该如何吗?如果是这样,究竟会发生什么?
答案 0 :(得分:6)
Per C 2011(草案N1570)7.1.3 1和2:
以下任何子条款中具有外部链接的所有标识符...始终保留用作具有外部链接的标识符。
如果程序在保留它的上下文中声明或定义标识符(除了7.1.4允许的标识符),或者将保留标识符定义为宏名称,则行为未定义。
“以下子条款”指定标准C库,包括strlen
。您的程序定义strlen
,因此其行为未定义。
您观察到的情况是:
strlen
应该如何表现,无论您的定义如何,因此,在strlen("abcd")
中优化f
时,它会在编译时评估strlen
,结果四分之一。g("abcd")
中,编译器无法识别,由于g
的定义,这等同于strlen("abcd")
,因此它不会在编译时对其进行优化。相反,它会将其编译为对g
的调用,并编译g
以调用strlen
,它还会编译您对strlen
的定义,结果为g("abcd")
1}}调用g
,调用您的strlen
,返回十。 C标准允许编译器完全放弃strlen
的定义,以便g
返回4。但是,一个好的编译器应该警告你的程序定义了一个保留的标识符。