Max*_*nft 24 c global-variables definition header-files forward-declaration
我目前想知道为什么在编译/链接小型C程序时我没有从GCC收到错误.
我在version.h
以下字符串中声明:
const char* const VERSION;
Run Code Online (Sandbox Code Playgroud)
在version.c
我已设置变量的初始化:
const char* const VERSION = "0.8 rev 213";
Run Code Online (Sandbox Code Playgroud)
没问题.我可以在程序的其余部分使用该字符串.
如果缺少c文件,则在编译/链接期间不会发生错误,但是当程序尝试访问变量时,程序会失败并且SIGSEGV(当然).
我设置变量的方法是VERSION
正确的还是有更好的方法?或者在编译/链接期间是否有机会出错?
n. *_* m. 31
您已在标头中定义(而不仅仅是声明)变量.
如果您从多个源文件中包含此标头,则行为未定义.这是标准的相关引用:
J.2未定义的行为
...
使用具有外部链接的标识符,但是在程序中,不存在标识符的正好一个外部定义,或者不使用标识符,并且标识符存在多个外部定义.
...
您依赖于GCC特定的(在许多编译器中通常是常见的,但仍然是非标准的)行为,这是合并重复的暂定数据定义.请参阅帮助-fcommon
和-fno-common
GCC编译标志.并非所有编译器都以这种方式运行.从历史上看,这是链接器的常见行为,因为这是Fortran在C之前的工作方式.
假设这种语言扩展,其中一个定义(具有显式初始化器的定义)初始化变量以指向您的字符串文字.但是如果省略这个定义,它将保持零初始化.也就是说,它将是一个常量空指针.不是很有用.
长话短说,永远不要那么做.要在标头中声明(但不定义)全局变量,请使用extern
.如果你这样做,并尝试在别处省略一个定义,你可能会收到一个链接器错误(虽然标准不需要诊断这个违规,所有已知的实现产生一个).
PSk*_*cik 15
你的例子是有效的,因为C(但不是C++)的Fortran启发(错误)特征称为暂定定义(6.9.2p2),它通常但非标准地扩展到多个文件.
暂定定义是extern
没有初始化器和没有初始化器的变量声明.在通常的实现中(双关语),暂定定义创建一种称为common
符号的特殊符号.在链接期间,在存在同名的常规符号的情况下,其他常见符号将成为常规符号的引用,这意味着VERSION
由于包含而在您的翻译单元中创建的所有空s将成为对常规符号的引用const char* const VERSION = "0.8 rev 213";
.如果没有这样的常规符号,则公共符号将合并为单个零初始化变量.
我不知道如何使用C编译器对此进行警告.
这个
const char* const VERSION;
const char* const VERSION = "0.8 rev 213";
Run Code Online (Sandbox Code Playgroud)
似乎与gcc一起工作无论我尝试过什么(g++
不会接受它 - C++没有暂定的定义功能,它不喜欢未明确初始化的const变量).但你可以编译-fno-common
(这是一个相当"常见"(强烈推荐)非标准选项(gcc,clang和tcc都有它))然后如果非初始化和初始化的extern-你会得到一个链接器错误 -较少的声明在不同的翻译单元中.
例:
VC:
const char * VERSION;
Run Code Online (Sandbox Code Playgroud)
main.c中
const char* VERSION;
int main(){}
Run Code Online (Sandbox Code Playgroud)
编译和链接:
gcc main.c v.c #no error because of tentative definitions
g++ main.c v.c #linker error because C++ doesn't have tentative definitions
gcc main.c v.c -fno-common #linker error because tentative defs. are disabled
Run Code Online (Sandbox Code Playgroud)
(const
为了C++示例,我在示例中删除了第二个--C++另外使const全局变量静态或类似的东西只会使临时定义功能的演示变得复杂.)
禁用暂定定义或使用C++时,标头中的所有变量声明都应包含extern
在其中
version.h中:
extern const char* const VERSION;
Run Code Online (Sandbox Code Playgroud)
并且每个全局应该只有一个定义,并且该定义应该有一个初始化器或者没有extern
(如果你应用于extern
初始化变量,一些编译器会发出警告).
version.c:
#include "version.h" //optional; for type checking
const char* const VERSION = "0.8 rev 213";
Run Code Online (Sandbox Code Playgroud)
在version.h
你应该宣布VERSION
像extern
一样
extern const char* const VERSION;
Run Code Online (Sandbox Code Playgroud)
在version.c
你应该定义外部变量之类的
const char* const VERSION = "0.8 rev 213";
Run Code Online (Sandbox Code Playgroud)
编辑: -您还需要确保只有一个源文件定义了变量, VERSION
而其他源文件声明了它 extern
,您可以通过在源文件中定义它VERSION.c
并将 extern
声明放在头文件中来完成VERSION.h
.