extern关键字的一点思考

在过去,我一直以为在不同的.c文件中引用全局变量需要使用extern关键词。突然发现,如下代码(不需要extern关键词)也可以直接运行(使用的是CLion,),并且不会出错。

//foo.c中
int a = 1;

//bar.c中
int a;
int main() {
   printf("%d\n",a);
}

这让我感觉十分诧异,为什么C语言运行同名的全局变量出现在多个不同的文件当中? 甚至,在同一个文件中定义也不会出现问题,查了不少资料。以防忘记,记录下来。

首先这个情况叫做tentaive definition,在c99标准中允许tentative definition的存在,在有些标准中是不允许的。这里提到

However, that is effectively a language extension. Strictly conforming ISO C programs cannot define a name more than once.

至于为什么需要这样做,上面的链接中也解释了是为了兼容c89,但是我没有仔细看。大概了解下。
那么,既然这样也可以使用,那么extern关键词是否显得有点多余?其实不是的,正儿八经来说,需要引用定义在其他文件中的全局变量,还会需要extern关键词,在本文的最后一处,介绍了如何合理的使用extern关键词的例子。

话说回来,虽然可以使用,但是还是需要尽量避免这种用法,因为在不同的编译器上可能有不同的行为从而导致代码的行为无法预测。引用自

As @litb points out here, and as stated in my answer to the cross-referenced question, using multiple definitions for a global variable leads to undefined behaviour, which is the standard's way of saying "anything could happen". One of the things that can happen is that the program behaves as you expect; and J.5.11 says, approximately, "you might be lucky more often than you deserve". But a program that relies on multiple definitions of an extern variable - with or without the explicit 'extern' keyword - is not a strictly conforming program and not guaranteed to work everywhere. Equivalently: it contains a bug which may or may not show itself.

这两种方式在文件的表述中是否有些不同的?

答案是有的,对于一个全局变量被声明了多次,但是只被初始化了一次这种情况(如果初始化和声明都多次,那么会报错)来说,被声明在文件当中,编译为.o文件后,该变量会位于common段当中,common 段这个用法来自fortran,用于在不同文件中共享变量来自

With some (indeed, many) C compilers, you can get away with what's called a 'common' definition of a variable too. 'Common', here, refers to a technique used in Fortran for sharing variables between source files, using a (possibly named) COMMON block. What happens here is that each of a number of files provides a tentative definition of the variable. As long as no more than one file provides an initialized definition, then the various files end up sharing a common single definition of the variable:

首先是,没有extern的情况,可以看到,a位于的段是common段截图如下:

不加extern

再来看一下加了extern的情况,此时可以看到a是UND的,意味着它并不是定义在当前文件当中的:

PS:我在WSL中实验的时候发现readelf -s得到的结果,不会出现,不知道是什么原因。在Linux下重新尝试了下,输出的结果是正常的(就是上图)。

解释这种情况为什么会发生

我上面编译的都是用gcc,WSL这gcc的版本是 gcc 9.3.0。在gcc 9当中,默认情况下
参数-fcommon使得可以让tentative definition通过链接。如果我们使用 -fno-common参数上述代码就无法通过。在gcc 10当中,-fno-common已经是默认了,也就是说上面的代码在gcc 10中无法通过链接。这两个参数有什么区别? 问了下知乎上的大神,来自

因此,在 -fcommon 参数下,bar.c 中的 a 在 common 块中,foo.c 中显式初始化的 a 在 data 段中,链接时则选择 data 段中 a,正常链接并运行。在 -fno-common 参数下,则 bar.c 产生 BSS 段的符号 a,foo.c 生成 data 段的 a,两个强符号产生冲突,链接错误。

作者:曾浩
链接:https://www.zhihu.com/question/442041186/answer/1706625116
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

如何正确的使用extern关键字

在stackoverflow的这篇回答中,作者很耐心的举例了如何使用。规则就是三条:

  1. 在头文件中带有extern关键词来声明(declaration)变量.
  2. 在需要这个变量的.c文件中引用这个头文件
  3. 在正式定义这个变量的.c文件中也需要引用这个头文件
    如下:

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

#include "file3.h"  /* Declaration made available here */
#include "prog1.h"  /* Function declarations */

/* Variable defined here */
int global_variable = 37;    /* Definition checked against declaration */

int increment(void) { return global_variable++; }

file2.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

References

Tentative definitions in C and linking
How do I use extern to share variables between source files?
Explanation of tentative definitions in C [duplicate]
About Tentative definition
What is the rationale behind tentative definitions in C?
GCC 10
一篇讲static,extern等关键词不错的短文

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇