背景:最近在交流中,有人问“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
下没有警告(更准确地说,与此问题无关的警告:它们仍然警告在我的strlen
定义中参数s
未使用)。有两个(相关的)问题(我认为它们足够相关,可以在同一个问题中提问):
- 在未包含声明它的头文件的情况下,是否允许重新定义C中的标准函数?
- 这个程序的行为是否符合预期?如果是,会发生什么?
gcc -Wall -Wstrict-prototypes -Werror
编译上述代码,它会报错,如“函数声明不是原型”。因此,我认为这明确了您提到的问题:“在未包含声明标准函数的头文件的情况下,是否允许重新定义C中的标准函数?” - Achal-Wstrict-prototypes
,谢谢,我学到了些东西。但是,如果我在f
和main
的参数中使用void
来消除这些警告,程序的行为没有改变(它仍然打印出4 10
),所以我不确定我是否理解你评论的最后一部分。 - Dadastrlen("abcd")
显然是编译器已经替换为值 4 的常量,因为它识别了strlen()
。然而,g("abcd")
不显然是常量,所以编译器会发出调用你的strlen()
函数的代码(但尝试将 g() 和 strlen() 声明为 static inline)。 - Yann Droneaud