Ela*_*ich 162 c syntax standards
在C中,我没有注意到extern函数声明之前使用的关键字的任何影响.起初,我认为extern int f();在单个文件中定义时会强制您在文件范围之外实现它.但是我发现两者都是:
extern int f();
int f() {return 0;}
Run Code Online (Sandbox Code Playgroud)
和
extern int f() {return 0;}
Run Code Online (Sandbox Code Playgroud)
编译得很好,没有gcc的警告.我用过gcc -Wall -ansi; 它甚至不接受//评论.
extern 在函数定义之前使用是否有任何影响?或者它只是一个可选的关键字,对函数没有副作用.
在后一种情况下,我不明白为什么标准设计师选择用多余的关键词来乱丢语法.
编辑:澄清一下,我知道extern变量的用法,但我只是extern在函数中询问.
Tim*_*ost 131
我们有两个文件,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;
}
Run Code Online (Sandbox Code Playgroud)
现在,这是bar.c
#include <stdio.h>
extern volatile unsigned int stop_now;
void bar_function(void)
{
while (! stop_now) {
printf("Hello, world!\n");
sleep(30);
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我们在foo.c和bar.c之间没有共享头,但是bar.c需要在链接时在foo.c中声明的内容,而foo.c在链接时需要bar.c中的函数.
通过使用'extern',您告诉编译器在链接时将找到其后的任何内容(非静态); 不要在当前通行证中保留任何内容,因为稍后会遇到它.在这方面,功能和变量被平等对待.
如果您需要在模块之间共享一些全局并且不想在标头中放置/初始化它,这非常有用.
从技术上讲,库公共头中的每个函数都是"extern",但是根据编译器的不同,将它们标记为几乎没有任何好处.大多数编译器都可以自己解决这个问题.如您所见,这些功能实际上是在其他地方定义的.
在上面的例子中,main()只会打印一次hello world,但会继续输入bar_function().另请注意,bar_function()在此示例中不会返回(因为它只是一个简单的示例).想象一下,当信号被服务(因此,易失性)时,stop_now被修改,如果这看起来不够实用.
Externs对于信号处理程序,你不想放在标题或结构中的互斥体等非常有用.大多数编译器都会进行优化以确保它们不为外部对象保留任何内存,因为它们知道它们将在定义对象的模块中保留它.然而,在为公共功能进行原型设计时,用现代编译器指定它并没有什么意义.
希望有帮助:)
小智 76
据我记得标准,默认情况下所有函数声明都被视为"extern",因此不需要明确指定它.
这不会使这个关键字无用,因为它也可以与变量一起使用(在这种情况下 - 它是解决链接问题的唯一解决方案).但是有了这些功能 - 是的,它是可选的.
小智 21
您需要区分两个独立的概念:函数定义和符号声明."extern"是一个链接修饰符,向编译器提示有关后面引用的符号定义的位置(提示是"不在这里").
如果我写
extern int i;
Run Code Online (Sandbox Code Playgroud)
在C文件中的文件范围(在功能块之外),然后你说"变量可能在别处定义".
extern int f() {return 0;}
Run Code Online (Sandbox Code Playgroud)
既是函数f的声明,也是函数f的定义.在这种情况下,定义超越了extern.
extern int f();
int f() {return 0;}
Run Code Online (Sandbox Code Playgroud)
首先是声明,然后是定义.
extern如果要声明并同时定义文件范围变量,则使用错误.例如,
extern int i = 4;
Run Code Online (Sandbox Code Playgroud)
将给出错误或警告,具体取决于编译器.
extern如果您明确要避免定义变量,则使用非常有用.
让我解释:
假设文件ac包含:
#include "a.h"
int i = 2;
int f() { i++; return i;}
Run Code Online (Sandbox Code Playgroud)
文件啊包括:
extern int i;
int f(void);
Run Code Online (Sandbox Code Playgroud)
并且文件bc包含:
#include <stdio.h>
#include "a.h"
int main(void){
printf("%d\n", f());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
标题中的extern非常有用,因为它在链接阶段告诉编译器,"这是一个声明,而不是定义".如果我删除定义i的ac中的行,为它分配空间并为其赋值,程序将无法使用未定义的引用进行编译.这告诉开发人员他已经引用了一个变量,但还没有定义它.另一方面,如果省略"extern"关键字,并删除该int i = 2行,程序仍然编译 - 我将使用默认值0定义.
如果没有为它们显式赋值,则隐式定义文件范围变量,默认值为0或NULL - 与您在函数顶部声明的块范围变量不同.extern关键字避免了这种隐式定义,因此有助于避免错误.
对于函数,在函数声明中,关键字确实是多余的.函数声明没有隐式定义.
dir*_*tly 14
该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 */
Run Code Online (Sandbox Code Playgroud)
以下是C99草案(n1256)的相关段落:
6.2.2标识符的链接
[...]
4对于在该标识符的先前声明可见的范围内使用存储类说明符extern声明的标识符,23)如果先前声明指定内部或外部链接,则后面声明中标识符的链接是相同的作为先前声明中指定的联系.如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接.
5如果函数的标识符声明没有存储类说明符,则确定其链接与使用存储类说明符extern声明的完全相同.如果对象的标识符声明具有文件范围而没有存储类说明符,则其链接是外部的.
IOW, extern 是多余的,什么也不做。
这就是为什么,10 年后:
extern以进行删除;git/git遵循该结论并extern从其代码中删除(对于 Git 2.22,2019 年第二季度)。请参阅提交 ad6dad0,提交 b199d71,提交 5545442(2019 年 4 月 29 日)由Denton Liu ( Denton-L)。
(由Junio C gitsterHamano合并-- --在commit 4aeeef3,2019 年 5 月 13 日)
*.[ch]:extern从函数声明中删除使用spatch一直在推动
extern从函数声明中删除。删除
externCoccinelle 捕获的函数声明的一些“ ”实例。
请注意,Coccinelle 在处理带有__attribute__或 varargs 的函数时有一些困难,因此一些extern声明被留下来在未来的补丁中处理。这是使用的 Coccinelle 补丁:
Run Code Online (Sandbox Code Playgroud)@@ type T; identifier f; @@ - extern T f(...);它是运行的:
Run Code Online (Sandbox Code Playgroud)$ git ls-files \*.{c,h} | grep -v ^compat/ | xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
但这并不总是直截了当的:
请参阅Denton Liu ( ) 的commit 7027f50 (04 Sep 2019 )。(由Denton Liu合并-- --在提交 7027f50 中,2019 年 9 月 5 日)Denton-L
Denton-L
compat/*.[ch]:extern使用 spatch 从函数声明中删除在 5545442(
*.[ch]:extern使用 spatch 从函数声明中删除,2019-04-29,Git v2.22.0-rc0)中,我们从函数声明中删除了 extern 使用,spatch但我们有意排除了下面的文件,compat/因为有些文件是直接从上游复制的,我们应该避免搅动它们,以便手动合并未来的更新会更简单。在最后一次提交中,我们确定了从上游获取的文件,以便我们可以排除它们并
spatch在其余文件上运行。这是使用的 Coccinelle 补丁:
Run Code Online (Sandbox Code Playgroud)@@ type T; identifier f; @@ - extern T f(...);它是运行的:
Run Code Online (Sandbox Code Playgroud)$ 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/Coccinelle 在处理
__attribute__和 varargs 时遇到了一些麻烦,因此我们运行以下命令以确保不会留下任何剩余的更改:Run Code Online (Sandbox Code Playgroud)$ 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/
请注意,在 Git 2.24(2019 年第四季度)中,任何虚假信息都会extern被删除。
请参阅Emily Shaffer ( ) 的提交 65904b8(2019 年 9 月 30 日)。
帮助者:杰夫·金 ( )。
请参阅Denton Liu ( ) 的提交 8464f94(2019 年 9 月 21 日)。
帮助者:杰夫·金 ( )。(由Junio C Hamano合并-- --在提交 59b19bc,2019 年 10 月 7 日)nasamuffinpeffDenton-Lpeff
gitster
promisor-remote.h:extern从函数声明中删除在这个文件的创建过程中,每次引入一个新的函数声明时,它都会包含一个
extern.
但是,从5545442(*.[ch]:extern使用spatch2019-04-29,Git v2.22.0-rc0从函数声明中删除)开始,我们一直在积极尝试防止在函数声明中使用 extern,因为它们是不必要的。删除这些虚假的
externs。
| 归档时间: |
|
| 查看次数: |
183083 次 |
| 最近记录: |