在头文件中使用extern的优点 [英] Advantage of using extern in a header file

查看:93
本文介绍了在头文件中使用extern的优点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里有一个类似的问题标题,但是在阅读答案时似乎并没有解决该特定问题:(它更像是为什么使用头文件?")./p>

extern 的以下用法中:

  extern int a;int b;//结构没有外部链接typedef struct Item_ {int id;} Item;extern项目Item_Extern;void main(void){}//稍后在程序或其他文件中int a = 4;int b = 5;项目Item_Extern = {1}; 

如果使用 extern ,编译器会产生任何不同的程序集吗?还是更多的是类型的元数据标签,以便代码(或编译器)的用户会清楚地知道该定义通常会在另一个文件中,或者在当前文件中更高版本中出现,否则,将是一个很难的错误.或者, extern 是否还做其他事情?

简介

这里有三种形式的关注声明: 1

  extern int x;//声明x但未定义.int x;//x的临时定义.int x = 0;//定义x. 

声明使标识符(名称,如 x )成为已知.

定义创建对象(例如 int ). 2 定义也是声明,因为它使名称已知.

在同一个翻译单元(正在编译的源文件及其所有包含的文件)中没有常规定义的暂定定义的行为就像初始化器为零的定义.

通常应使用的方式是:

实际上,通常应该使用这些方法是根本不使用它们.程序通常不需要对象的外部标识符,因此您应在没有它们的情况下设计程序.上面的规则适用于您何时使用它们.

暂定定义

在Unix和其他一些系统中,可以在头文件中放置一个临时定义 int x; ,并将其包含在多个源文件中.因为暂定定义在没有常规定义的情况下的作用类似于定义,所以这导致在多个翻译单元中存在多个定义.C标准未定义其行为.那么它在Unix中如何工作?

直到最近,当您使用GCC进行编译时,它都创建了一个目标文件,该文件标记了与常规定义的标识符不同的临时定义的标识符.临时定义的标识符标记为通用".当链接程序找到公共"标识符的多个定义时,它将它们合并为一个定义.请记住,C标准没有定义行为.但是Unix工具 4 做到了.因此,您可以将 int x; 放在标头中,并将其包含在很多地方,并且在链接整个程序时会从中获得一个 int x .

GCC的最新版本默认情况下不执行此操作.在没有常规定义的情况下,将临时定义更像常规定义一样对待,并且将多个定义与同一标识符链接在一起将导致错误,即使这些定义是由临时定义引起的.GCC可以选择旧的行为 -fcommon .

这是您应该知道的信息,以便您可以了解利用常见"行为的旧源文件和标头.在新的源代码中不需要它,并且您应该仅在标头和源文件中的常规定义中编写非定义声明(使用 extern ).

其他

您不需要带有函数声明的 extern ,因为没有主体(包含函数代码的复合语句)的函数声明自动是一个声明,其行为与具有 extern .函数没有临时定义.

脚注

1 此答案仅解决具有外部链接的对象标识符的外部声明和外部定义.C声明的完整规则有些复杂,部分原因是C的发展历史.

2 这是用于引用对象的标识符的定义.对于其他种类的标识符,定义可能不同.例如,据说 typedef int foo 定义了 foo 作为类型 int 的别名,但是没有创建对象.

3 最好也包含一个初始化器,即使它为零,因为这将使它成为常规定义,并且避免了在两个临时名称中使用相同名称的潜在问题.两种不同事物的不同源文件,即使这是一个错误,链接器也不会抱怨.

4 我这里的术语可能很草率;其他人可以准确地确定在何处指定了此行为以及将其应用于什么工具.

There is a similar question title here, but in reading the answers it doesn't seem to address that particular question: C: What is the use of 'extern' in header files? (it's more like a "Why use header files?").

In the following usages of extern:

extern int a;
int b;

// structs have no external linkage 
typedef struct Item_ {int id;} Item;
extern Item Item_Extern;

void main(void) {

}

// later in the program or another file
int a=4;
int b=5;
Item Item_Extern = {1};

Does the compiler produce any different assembly if extern is used, or is this more a metadata-tag for a type so that a user of the code (or compiler) will know explicitly that the definition will come in a different file (usually) or later on in the current file, and if it doesn't it'll be a hard error. Or, does extern 'do' anything else?

解决方案

Introduction

There are three forms of declaration of concern here:1

extern int x; // Declares x but does not define it.
int x;        // Tentative definition of x.
int x = 0;    // Defines x.

A declaration makes an identifier (a name, like x) known.

A definition creates an object (such as an int).2 A definition is also a declaration, since it makes the name known.

A tentative definition without a regular definition in the same translation unit (the source file being compiled, with all of its included files) acts like a definition with an initializer of zero.

The way you should use these normally is:

  • For an object you will access by name in multiple files, write exactly one definition of it in one source file. (It can be a tentative definition3 if you would like it to be initialized with zero, or it can be a regular definition with an initializer you choose.)
  • In an associated header file (such as foo.h for the source file foo.c), declare the name, using extern as shown above.
  • Include the header file in each file that uses the name, including its associated source file. (The latter is important; when foo.c includes foo.h, the compiler will see both the declaration and the definition in the same compilation and give you a warning if there is a typo that makes the two declarations incompatible.)

Actually, the way you should use these normally is not to use them at all. Programs generally do not need external identifiers for objects, so you should design the program without them. The rules above are for when you do use them.

Tentative Definitions

In Unix and some other systems, it has been possible to put a tentative definition, int x;, in a header file and include it in multiple source files. Because a tentative definition acts like a definition in the absence of a regular definition, this results in there being multiple definitions in multiple translation units. The C standard does not define the behavior of this. So how does it work in Unix?

Until recently, when you compiled with GCC, it created an object file that marked tentatively defined identifiers differently from regularly defined identifiers. The tentatively defined identifiers were marked as "common." When the linker found multiple definitions of a "common" identifier, it coalesced them into a single definition. Remember, the C standard does not define the behavior. But Unix tools4 did. So you could put int x; in a header and include it in lots of places, and you would get one int x out of it when linking the entire program.

Recent versions of GCC do not do this by default. Tentative definitions are, in the absence of regular definitions, treated more like regular definitions, and linking with multiple definitions of the same identifier will result in an error, even if the definitions arose from tentative definitions. GCC has a switch to select the old behavior, -fcommon.

This is information you should know so that you can understand old source files and headers that took advantage of the "common" behavior. It is not needed in new source code, and you should write only non-definition declarations (using extern) in headers and regular definitions in source files.

Miscellaneous

You do not need extern with a function declaration because a function declaration without a body (the compound statement that contains the code for the function) is automatically a declaration and behaves the same as if it had extern. Functions do not have tentative definitions.

Footnote

1 This answer addresses only external declarations and external definitions for identifiers of objects, with external linkage. The full rules for C declarations are somewhat complicated, partly due to the history of C’s evolution.

2 This is for definitions of identifiers that refer to objects. For other kinds of identifiers, what is a definition may be different. For example, typedef int foo is said to define foo as an alias for the type int, but no object is created.

3 It may be preferable to also include an initializer, even if it is zero, as this will make it a regular definition and avoid a potential problem where the same name is used tentative definitions in two different source files for two different things, resulting in the linker not complaining even though this is an error.

4 I may be being sloppy with terminology here; somebody else could identify precisely where this behavior was specified and what tools it applied to.

这篇关于在头文件中使用extern的优点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆