考虑由两个文件组成的C程序
F1.C:
int x;
F2.C:
int x=2;
我对 C99标准应该拒绝该程序。在我对6.9.2的解释中,变量 x
是在 f1.c
中暂定定义的,但是此暂定定义在翻译单元末尾成为实际定义,并且(我认为)因此,应表现得好像 f1.c
包含定义 int x = 0;
。
有了所有编译器(重要的是,我都可以尝试),这不是发生的事情。我尝试过的所有编译平台都链接上述两个文件, x
的值在两个文件中为2。
我怀疑这种情况是偶然发生的,或者只是作为标准所需的“简单”功能。如果您考虑一下,这意味着链接器在链接器中为那些没有初始化器的全局变量提供了特殊的支持,而不是明确初始化的那些变量。有人告诉我,无论如何,链接器功能可能是必要的。那将是一个合理的解释。
对此有什么想法吗?标准的其他解释?哪些文件的平台 f1.c
和 f2.c
拒绝将其链接在一起?
注意:这很重要,因为问题发生在静态分析的背景下。如果两个文件可能拒绝在某些平台上链接,则分析器应抱怨,但是如果每个汇编平台都接受它,则没有理由警告它。
Consider the C program composed of two files,
f1.c:
int x;
f2.c:
int x=2;
My reading of paragraph 6.9.2 of the C99 standard is that this program should be rejected. In my interpretation of 6.9.2, variable x
is tentatively defined in f1.c
, but this tentative definition becomes an actual definition at the end of the translation unit, and (in my opinion), should therefore behave as if f1.c
contained the definition int x=0;
.
With all compilers (and, importantly, linkers) I was able to try, this is not what happens. All compilation platforms I tried do link the above two files, and the value of x
is 2 in both files.
I doubt this happens by accident, or just as an "easy" feature to provide in addition to what the standard requires. If you think about it, it means there is special support in the linker for those global variables that do not have an initializer, as opposed to those explicitly initialized to zero. Someone told me that the linker feature may be necessary to compile Fortran anyway. That would be a reasonable explanation.
Any thoughts about this? Other interpretations of the standard? Names of platforms on which files f1.c
and f2.c
refuse to be linked together?
Note: this is important because the question occurs in the context of static analysis. If the two files may refuse to be linked on some platform, the analyzer should complain, but if every compilation platform accepts it then there is no reason to warn about it.
发布评论
评论(3)
另请参见 c 中的外部变量是什么。在信息附件J中的C标准中提到了这一点,这是一个共同的扩展:
正如@litb在这里指出的那样
,正如我对交叉引用问题的回答所述,使用多个定义的全局变量导致行为不确定,这是标准说“任何事情都可能发生”的方式。可能发生的事情之一是该程序的行为按照您的期望。 J.5.11大约说:“您可能幸运的频率超过应有的。”但是,一个依赖于外部变量的多个定义的程序 - 有无明确的
extern
关键字 - 不是一个严格符合的程序,也不能保证到处工作。等效地:它包含一个 bug ,可能会或可能不会表现出来。另请参阅我如何使用
extern
在源文件之间共享变量?如 Sven 和链接/1490777#comment116437537_1490693“>注释,在我对“我如何使用
extern
…”的回答中,GCC更改了其默认规则。在 gcc 10.x 10.x 10.x (从2020年5月起)和后来的版本,默认版本汇编模式使用-fno-common
,而在先前的版本中,使用的默认模式-fcommon
。新行为意味着您不会逃脱多个暂定定义,这是C标准对严格符合的要求。如果您使用GCC并将(AB)使用多个暂定定义的代码,则您 can 添加
-fcommon
到汇编过程中,它将像以前一样工作。但是,您的代码不是最大的便携式,并且长期修改代码更好,以便在一个源文件中正确定义每个变量(与所有需要使用该变量的程序链接)并正确地定义了代码。在一个标题中声明,使用该变量的源文件都可以包括(定义变量的源文件还应包括标题以确保一致性)。See also What are extern variables in C. This is mentioned in the C standard in informative Annex J as a common extension:
Warning
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.See also How do I use
extern
to share variables between source files?As noted by Sven in a comment, and in my answer to "How do I use
extern
…", GCC changed its default rules relatively recently. In GCC 10.x (from May 2020) and later versions, the default compilation mode uses-fno-common
whereas in prior versions the default mode used-fcommon
. The new behaviour means that you do not get away with multiple tentative definitions, which is what the C standard requires for strict conformance.If you use GCC and have code that (ab)uses multiple tentative definitions, you can add
-fcommon
to the compilation process and it will work as before. However, your code is not maximally portable, and it would be better for the long-term to revise the code so that each variable is properly defined in one source file (that is linked with all programs that need to use that variable) and properly declared in one header that the source files which use the variable can all include (and the source file defining the variable should also include the header to ensure consistency).标准有一个称为“公共扩展”的东西,只要变量仅初始化一次,就可以多次定义变量。参见 http://c-faq.com/dec.com/decl/decldef.html
链接页面说这与Unix平台有关 - 我猜C99与C89相同 - 尽管更多编译器已经采用了形成某种DefaTso标准的编译器。有趣的。
There is something called a "common extension" to the standard, where defining variables multiple times is allowed as long as the variable is initialized only once. See http://c-faq.com/decl/decldef.html
The linked page says this is pertinent to Unix platforms--I guess it's the same for c99 as c89--though maybe it has been adopted by more compilers to form some sort of a defacto standard. Interesting.
This is to clarify my answer to a comment by olovb:
output of nm for an object file compiled from "int x;".在此平台上,用'_'预及符号,即变量x以_x的形式出现。
从“ int x = 1;”编译的对象文件的NM输出的输出
从“ int x = 0;”编译的对象文件的NM输出的输出
从“ extern int x”编译的对象文件的NM输出;
编辑:从“ extern int x”编译的对象文件的NM输出;其中X实际使用的功能之一
This is to clarify my answer to a comment by olovb:
output of nm for an object file compiled from "int x;". On this platform, symbols are prepended with a '_', that is, the variable x appears as _x.
output of nm for an object file compiled from "int x=1;"
output of nm for an object file compiled from "int x=0;"
output of nm for an object file compiled from "extern int x;"
EDIT: output of nm for an object file compiled from "extern int x;" where x is actually used in one of the functions