extern关键字对C函数的影响
在C中,我没有注意到函数声明之前使用的extern关键字对C函数的影响,c,syntax,standards,C,Syntax,Standards,在C中,我没有注意到函数声明之前使用的extern关键字的任何效果。 起初,我认为在定义extern int f()时在单个文件中强制您在文件范围之外实现它。然而,我发现这两个方面: extern int f(); int f() {return 0;} 及 编译很好,没有来自gcc的警告。我使用了gcc-Wall-ansi;它甚至不接受/注释 在函数定义之前使用extern是否有任何影响?或者它只是一个可选的关键字,对函数没有副作用 在后一种情况下,我不明白为什么标准设计者选择在语法中添加多
extern
关键字的任何效果。
起初,我认为在定义extern int f()时代码>在单个文件中强制您在文件范围之外实现它。然而,我发现这两个方面:
extern int f();
int f() {return 0;}
及
编译很好,没有来自gcc的警告。我使用了gcc-Wall-ansi
;它甚至不接受/
注释
在函数定义之前使用extern
是否有任何影响?或者它只是一个可选的关键字,对函数没有副作用
在后一种情况下,我不明白为什么标准设计者选择在语法中添加多余的关键字
编辑:为了澄清,我知道变量中有extern
的用法,但我只是询问函数中的extern
关键字extern
通知编译器函数或变量具有外部链接-换句话说,它在定义它的文件以外的文件中可见。从这个意义上讲,它与static
关键字的含义相反。在定义时放置extern
有点奇怪,因为没有其他文件可以看到定义(或者会导致多个定义)。通常,您将extern
放在具有外部可见性的某个点的声明中(例如头文件),然后将定义放在其他位置。据我所知,默认情况下,所有函数声明都被视为“extern”,因此无需显式指定它
extern int f() {return 0;}
这并没有使这个关键字变得无用,因为它也可以与变量一起使用(在这种情况下,它是解决链接问题的唯一解决方案)。但是对于函数,是的,它是可选的。我们有两个文件,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;
}
#包括
易失性无符号int stop\u now=0;
外部无效条函数(void);
内部主(空)
{
而(1){
bar_函数();
立即停止=1;
}
返回0;
}
现在,这里是bar.c
#include <stdio.h>
extern volatile unsigned int stop_now;
void bar_function(void)
{
if (! stop_now) {
printf("Hello, world!\n");
sleep(30);
}
}
#包括
extern volatile unsigned int stop_now;
void bar_函数(void)
{
如果(!立即停止){
printf(“你好,世界!\n”);
睡眠(30);
}
}
正如您所看到的,我们在foo.c和bar.c之间没有共享头,但是bar.c在链接时需要在foo.c中声明的内容,而foo.c在链接时需要bar.c中的函数
通过使用“extern”,您告诉编译器,它后面的任何内容都将在链接时找到(非静态);不要在当前过程中为它保留任何内容,因为稍后会遇到它。函数和变量在这方面被同等对待。
如果您需要在模块之间共享一些全局数据,并且不想将其放入/初始化到头中,那么它非常有用
从技术上讲,库公共头中的每个函数都是“extern”,但是根据编译器的不同,将它们标记为extern几乎没有什么好处。大多数编译器可以自己解决这个问题。如您所见,这些函数实际上是在其他地方定义的
在上面的示例中,main()只打印hello world一次,但继续输入bar_function()。还要注意,在本例中,bar_function()不会返回(因为它只是一个简单的示例)。试想一下,如果这看起来不够实用,那么当一个信号被服务时(因此是不稳定的),stop_正在被修改
extern对于信号处理程序、不希望放入头或结构中的互斥体等非常有用。大多数编译器都会进行优化,以确保它们不会为外部对象保留任何内存,因为它们知道它们将在定义对象的模块中保留内存。然而,同样,在原型化公共函数时,用现代编译器指定它没有什么意义
希望对您有所帮助:)根据环境的不同,extern
关键字有不同的形式。如果有可用的声明,则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”是一个链接修饰符,向编译器提示后面提到的符号的定义位置(提示是“nothere”)
如果我写
extern int i;
在C文件的文件范围(函数块之外)中,则表示“变量可能在其他地方定义”
是函数f的声明和函数f的定义。本例中的定义凌驾于外部
extern int f();
int f() {return 0;}
首先是声明,然后是定义
如果要声明并同时定义文件,则使用extern
是错误的
extern int f();
int f() {return 0;}
extern int i = 4;
#include "a.h"
int i = 2;
int f() { i++; return i;}
extern int i;
int f(void);
#include <stdio.h>
#include "a.h"
int main(void){
printf("%d\n", f());
return 0;
}
@@
type T;
identifier f;
@@
- extern
T f(...);
$ git ls-files \*.{c,h} |
grep -v ^compat/ |
xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
@@
type T;
identifier f;
@@
- extern
T f(...);
$ git ls-files compat/\*\*.{c,h} |
xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
$ git checkout -- \
compat/regex/ \
compat/inet_ntop.c \
compat/inet_pton.c \
compat/nedmalloc/ \
compat/obstack.{c,h} \
compat/poll/
$ git ls-files compat/\*\*.{c,h} |
xargs sed -i'' -e 's/^\(\s*\)extern \([^(]*([^*]\)/\1\2/'
$ git checkout -- \
compat/regex/ \
compat/inet_ntop.c \
compat/inet_pton.c \
compat/nedmalloc/ \
compat/obstack.{c,h} \
compat/poll/
int f() {return 0;}
extern int f() {return 0;}