extern关键字对C函数的影响
在C中,我没有注意到函数声明之前使用的extern
关键字的任何影响。 起初,我认为当定义extern int f();
在单个文件中强制你在文件范围之外实现它。 但是我发现这两个:
extern int f(); int f() {return 0;}
和
extern int f() {return 0;}
编译得很好,没有来自gcc的警告。 我用gcc -Wall -ansi
; 它甚至不会接受/ /评论。
函数定义之前使用extern
有没有影响? 或者它只是一个可选的关键字,对function没有副作用。
在后一种情况下,我不明白为什么标准devise人员select用多余的关键字来抛弃语法。
编辑:澄清,我知道有variables的extern
的用法,但我只是在function问extern
。
我们有两个文件,foo.c和bar.c.
这里是foo.c
#include <stdio.h> volatile unsigned int stop_now = 0; extern void bar_function(void); int main(void) { while (1) { bar_function(); stop_now = 1; } return 0; }
现在,这里是bar.c
#include <stdio.h> extern volatile unsigned int stop_now; void bar_function(void) { while (! stop_now) { printf("Hello, world!\n"); sleep(30); } }
正如你所看到的,我们在foo.c和bar.c之间没有共享头文件,但是bar.c需要在链接时在foo.c中声明的东西,而foo.c在链接时需要bar.c中的一个函数。
通过使用'extern',你告诉编译器,在链接时发现的任何东西(非静态),不要为它保留任何东西,因为它会在稍后遇到。
这是非常有用的,如果你需要共享模块之间的一些全球性,而不想把/初始化在一个头。
从技术上讲,图书馆公共标题中的每个函数都是“extern”,但是根据编译器,标记它们几乎没有任何好处。 大多数编译器可以自行解决这个问题。 如你所见,这些函数实际上是在别的地方定义的。
在上面的例子中,main()只打印一次hello world,但是继续inputbar_function()。 还要注意,在这个例子中bar_function()不会返回(因为这只是一个简单的例子)。 试想一下,当信号被服务时,stop_now被修改(因此,volatile),如果这似乎不够实际。
Extern对于信号处理程序,你不想放在头文件或结构中的互斥体等是非常有用的。大多数编译器将进行优化,以确保它们不为外部对象保留任何内存,因为他们知道它们将保留在定义对象的模块中。 然而,在原型devise公共职能时,用现代编译器来指定它却毫无意义。
希望帮助:)
就我所记得的标准而言,所有的函数声明在默认情况下都被认为是“extern”,所以没有必要明确地指定它。
这不会使这个关键字无用,因为它也可以与variables一起使用(并且这种情况 – 这是解决链接问题的唯一解决scheme)。 但是有了这些function – 是的,它是可选的。
extern
关键字根据环境而有不同的forms。 如果声明可用,则extern
关键字将按照之前在翻译单元中指定的方式进行链接。 在没有这样的声明的情况下, extern
指定了外部链接。
static int g(); extern int g(); /* g has internal linkage */ extern int j(); /* j has tentative external linkage */ extern int h(); static int h(); /* error */
以下是C99草案(n1256)的相关段落:
6.2.2标识符的链接
[…]
4对于在该标识符的在先声明可见的范围内的存储类别说明符extern声明的标识符,23)如果在先声明指定了内部或外部链接,则稍后声明中标识符的链接是相同的作为事先声明中指定的联系。 如果前面的声明不可见,或者前面的声明没有指定链接,则标识符具有外部链接。
5如果一个函数的标识符声明没有存储类说明符,那么它的链接就像使用存储类说明符extern声明一样。 如果对象的标识符的声明具有文件范围并且没有存储类说明符,则其链接是外部的。
您需要区分两个单独的概念:函数定义和符号声明。 “extern”是一个链接修饰符,它提示编译器定义了之后引用的符号的位置(提示是“不在这里”)。
如果我写
extern int i;
在C文件中的文件范围内(function块外),则表示“该variables可能在别处定义”。
extern int f() {return 0;}
既是函数f的声明又是函数f的定义。 在这种情况下,定义在外部。
extern int f(); int f() {return 0;}
首先是一个声明,然后是定义。
如果要声明并同时定义文件范围variables,则使用extern
是错误的。 例如,
extern int i = 4;
将根据编译器给出错误或警告。
如果明确地要避免定义variables,则extern
用法很有用。
让我解释:
假设文件ac包含:
#include "ah" int i = 2; int f() { i++; return i;}
该文件啊包括:
extern int i; int f(void);
而文件bc包含:
#include <stdio.h> #include "ah" int main(void){ printf("%d\n", f()); return 0; }
头文件中的extern是有用的,因为它在链接阶段告诉编译器,“这是一个声明,而不是一个定义”。 如果我删除了定义为i的ac行,为它分配空间并为其赋值,则程序将无法编译一个未定义的引用。 这告诉开发者他已经提到了一个variables,但还没有定义它。 如果另一方面,我省略了“extern”关键字,并删除int i = 2
行,程序仍然编译 – 我将被定义为默认值为0。
文件范围variables是隐式定义的,默认值为0或NULL,如果您没有明确地赋值给它们 – 与您在函数顶部声明的块范围variables不同。 extern关键字避免了这个隐式定义,从而有助于避免错误。
对于函数,在函数声明中,关键字确实是多余的。 函数声明没有隐式定义。
内联函数对于什么是extern
手段有特殊的规定 。 (请注意,内联函数是C99或GNU扩展;它们不是原来的C.
对于非内联函数, extern
不需要,因为它默认是打开的。
请注意,C ++的规则是不同的。 例如,你要从C ++调用的C函数的C ++声明中需要extern "C"
,并且关于inline
有不同的规则。
extern
关键字通知编译器该函数或variables具有外部链接 – 换句话说,它是从除定义的文件以外的文件中可见的。 从这个意义上讲,它与static
关键字有相反的含义。 在定义的时候放置extern
,因为没有其他文件能够看到定义(或者会导致多个定义)。 通常情况下,您可以在外部可见性(例如头文件)的某个位置将extern
放在声明中,并将定义放在其他位置。
声明一个extern函数意味着它的定义将在链接时解决,而不是在编译期间解决。
不像常规的函数,它没有声明为extern,它可以在任何源文件中定义(但不能在多个源文件中,否则你会得到链接器错误,说你已经给出了函数的多个定义),包括它被声明为extern。所以,在这种情况下,链接器parsing了同一个文件中的函数定义。
我不认为这样做会很有用,但是通过这样的实验可以更好地了解语言的编译器和链接器是如何工作的。
它没有效果的原因是因为在链接时链接器试图解决外部定义(在你的情况下extern int f()
)。 只要发现它在同一个文件或不同的文件中find它并不重要。
希望这回答你的问题。