我是C语言的新手,读到每个函数只能定义一次,但我似乎无法将这与我在控制台中看到的一致。例如,我可以覆盖printf
的定义,而不会出现错误或警告:
#include <stdio.h>
extern int printf(const char *__restrict__format, ...) {
putchar('a');
}
int main() {
printf("Hello, world!");
return 0;
}
因此,我试着在标准中查找一个定义规则,并在第155页找到了第6.9(5)节,其中说(着重号是添加的):
一个 external definition 是一个外部声明,它也是一个函数的定义(而不是一个内联定义)或一个对象的定义。如果一个用外部链接表示的标识符被用在一个表达式[...]中,在整个程序的某个地方应该有一个正好是这个标识符的外部定义;否则,不得超过一个。
我对链接的理解是非常不可靠的,所以我不确定这是否是相关的子句,或者“整个程序”的确切含义是什么。但是,如果我把“整个程序”理解为<stdio.h>
+我的源文件中的所有内容,那么是否应该禁止我在源文件中重新定义printf
,因为它已经在“整个程序”中定义过了(即在程序的stdio
位中)?
我很抱歉,如果这个问题是一个傻瓜,我找不到任何现有的答案。
2条答案
按热度按时间bis0qfac1#
C标准没有定义如果函数有多个定义会发生什么。
......我是不是应该被禁止......
C标准对你做什么没有管辖权。它规定了如何解释C程序,而不是人类可以如何行为。虽然它的一些规则是用“shall”写的,但这不是对程序员的命令,告诉他们可以做什么或不可以做什么。它是一种修辞手段,用于指定C程序的语义。C 2018 4 2告诉我们它的实际含义:
如果出现在约束或运行时约束之外的“应”或“不应”要求被违反,则行为未定义...
因此,当您提供
printf
的定义,而标准C程式库提供printf
的定义时,C标准并不会指定会发生什麽。在一般的实践中,可能会发生下列几种情况:printf
。不会使用程式库中的printf
。printf
的内置知识,并且不管您对printf
的定义如何,都使用该知识。printf
位于单独的源模块中,并且该模块被编译并插入到库中,则程序使用哪个printf
取决于向链接器指定库的顺序。虽然C标准没有定义函数(或一般的外部符号)有多个定义时会发生什么,但链接器通常会这样做。通常,当链接器处理库文件时,其行为是:
因此,对于普通函数,出现在库文件中的多个定义的行为是由链接器定义的,即使它不是由C标准定义的。(但是可能会有一些复杂的情况,假设一个程序使用
cos
和sin
,并且当链接器发现定义了sin
和cos
两者的库模块时,链接器已经包括了定义了cos
的模块。因为连接器有sin
的未解析指涉,所以它会包含这个程式库模块,这会引入cos
的第二个定义,造成多重定义错误)。尽管链接器的行为可能已经定义好了,但仍然存在编译器内置了标准库函数的问题。考虑this example。在这里,我添加了第二个
printf
,所以程序具有:程式输出为“aHello,world.\n”。这表示程式在第一个
printf
呼叫中使用您的定义,但在第二个printf
呼叫中使用标准行为。程式的行为就像是在同一个程式中有两个不同的printf
定义。对于第二次调用,编译器决定,由于
printf("Hello, world!\n");
打印的字符串没有转换规范,并且以换行符结尾,它可以使用效率更高的puts
例程,所以汇编语言使用call puts
作为第二个printf
。编译器无法对第一个printf
执行此操作,因为它没有以换行符结尾,而puts
会自动添加换行符。roqulrg32#
请注意
declaration
和definition
。这两个术语完全不同。declaration
。因此,当你在文件中声明/定义时,只要原型是相似的,就可以这样做。