我的理解是,在链接时,如果符号是用'extern'关键字声明的,那么编译器不会抱怨没有它的定义。此外,链接器也不会有任何问题,因为它首先会创建符号表,然后会检测到正确的源代码并填充其定义。
构建一个由多个文件组成的没有头文件的程序似乎完全没有问题——如果一个文件中所有不存在的符号都用'extern'声明好。
如果用'extern'关键字就可以检测到其他文件符号的识别,那为什么还要有'header'文件呢?
我的理解是,在链接时,如果用'extern'关键字声明符号,那么编译器不会抱怨没有它的定义。
C 中的
extern
关键字告诉编译器,它所在的声明描述了标识符,但没有定义它们。1 这是编译时的效果,而不是链接时发生的事情。
如果用'extern'关键字就可以检测到其他文件符号的识别,那为什么还要有'header'文件呢?
将声明放在头文件中允许我们将它们包含在多个源文件中,而无需重新键入它们或复制和粘贴它们。这避免了键入错误,并有助于确保在整个程序中对标识符使用相同的声明。此外,最好在定义标识符的源文件中包含声明标识符的头文件,以便编译器在同一编译中看到声明和定义,并在它们不一致时发出警告。
另一个原因是头文件被认为是相关源文件的作者发布的东西,告诉其他程序员源文件提供了什么。在程序员可以在自己的源文件中使用
extern
编写自己的声明之前,必须有人告诉他们这些声明是什么。编写源文件 NiftyLibrary.c 的人可以(并且应该)编写一份手册,列出所有函数的声明(以及这些函数的作用),然后任何使用 NiftyLibrary.c 的人都可以阅读该手册并将必要的声明键入他们的程序。但是如果库作者提供 NiftyLibrary.h 并且其他程序员只包含标题而不是重新输入所有内容,那就更容易了。
1这是
extern
的功能之一。由于 C 的发展历史,完整的语义很复杂。
函数原型确实在 C 标准中定义,因此与其从标准头文件中获取,不如在使用前将它们写入源代码中。相反,头文件还定义了结构、类型和宏,这些结构、类型和宏可能因系统而异或取决于编译器或库配置。对于这些,标准标头是唯一可靠的来源。
例如,经典程序hello.c可以在不包含任何标准头文件的情况下编写为可移植的:
extern int printf(const char *format, ...);
int main(void) {
printf("Hello world!\n");
return 0;
}
当然原型必须与实际实现兼容,否则即使编译没有任何错误,运行程序时也会出现未定义的行为。一个兼容的原型是:
extern int printf(const char format[], ...);
但是将
printf
定义为 extern int printf(const char *format);
是不正确的,即使您从不使用多个字符串参数调用该函数。
反之,如果要用
fopen
打开文件,FILE
的定义必须来自<stdio.h>
。同样,如果没有至少一个定义类型 malloc
.的标头,则不能声明
size_t