Answers:
这static
意味着将为其中VAL
包含的每个源文件创建一个副本。但是,这也意味着多个包含不会导致该对象的多个定义VAL
在链接时发生冲突。在C中,如果没有,则static
需要确保仅定义一个源文件,VAL
而其他源文件声明它extern
。通常,这是通过在源文件中定义它(可能使用初始化程序)并将extern
声明放入头文件中来实现的。
static
全局变量仅在自己的源文件中可见,无论它们是通过include到达还是在主文件中。
编者注:在C ++中,声明中const
既没有static
nor也没有extern
关键字的对象是隐式的static
。
文件作用域变量上的static
和extern
标记确定它们是否可以在其他翻译单元(即其他.c
或.cpp
文件)中访问。
static
给出变量内部链接,将其对其他翻译单元隐藏。但是,具有内部链接的变量可以在多个翻译单元中定义。
extern
给出可变的外部链接,使其对其他翻译单元可见。通常,这意味着只能在一个翻译单元中定义变量。
默认值(不指定static
或时extern
)是C和C ++不同的区域之一。
在C中,extern
默认情况下,文件作用域变量是(外部链接)。如果您使用的是C,VAL
则is static
和ANOTHER_VAL
is extern
。
在C ++中,文件范围的变量是static
(内部连接)默认如果它们const
,并extern
通过缺省,如果他们不。如果您使用的是C ++,则VAL
和ANOTHER_VAL
均为static
。
从C规范的草稿中:
6.2.2标识符的链接... -5-如果一个函数的标识符声明没有存储类说明符,则其链接的确定与使用存储类说明符extern声明的完全相同。如果对象的标识符声明具有文件范围且没有存储类说明符,则其链接是外部的。
根据C ++规范的草案:
7.1.1-存储类说明符[dcl.stc] ... -6-在命名空间范围中声明的没有存储类说明符的名称具有外部链接,除非由于先前的声明而具有内部链接,并且没有声明的常量。声明为const且未明确声明为extern的对象具有内部链接。
静态表示每个文件一个副本,但是与其他文件不同,这样做是完全合法的。您可以使用一个小的代码示例轻松地对此进行测试:
test.h:
static int TEST = 0;
void test();
test1.cpp:
#include <iostream>
#include "test.h"
int main(void) {
std::cout << &TEST << std::endl;
test();
}
test2.cpp:
#include <iostream>
#include "test.h"
void test() {
std::cout << &TEST << std::endl;
}
运行此命令可以得到以下输出:
0x446020
0x446040
TEST
是const
LTO是否能够将其优化到单个内存位置。但是-O3 -flto
GCC 8.1没有。
const
C ++中的变量具有内部链接。因此,使用static
无效。
啊
const int i = 10;
一个cpp
#include "a.h"
func()
{
cout << i;
}
二.cpp
#include "a.h"
func1()
{
cout << i;
}
如果这是C程序,您将i
由于(由于外部链接)而出现“多个定义”错误。
static
的效果是它可以整洁地传达意图和对编码内容的意识,这绝不是一件坏事。在我看来,这包括virtual
覆盖时:我们不必这样做,但是当我们这样做时,事情看起来更加直观-并且与其他声明一致。
此代码级别的静态声明意味着variabel仅在当前编译单元中可见。这意味着只有该模块中的代码才能看到该变量。
如果您有一个头文件声明了一个静态变量,并且该头文件包含在多个C / CPP文件中,则该变量对于这些模块将是“本地”的。对于包含标头的N个位置,该变量将有N个副本。它们根本不相关。这些源文件中任何一个中的任何代码都只会引用该模块中声明的变量。
在这种情况下,“静态”关键字似乎没有提供任何好处。我可能会丢失一些东西,但这似乎无关紧要-以前我从未见过这样的事情。
至于内联,在这种情况下,该变量很可能是内联的,但这仅是因为它已声明为const。编译器可能更容易内联模块静态变量,但这取决于情况和正在编译的代码。不能保证编译器会内联“ statics”。
const
,static
因此暗含了,因此是可选的。推论是,正如迈克·F所声称的那样,不存在多重定义错误的敏感性。
C语言书籍(在线免费)上有一个关于链接的章节,该章更详细地解释了“静态”的含义(尽管其他注释中已经给出了正确的答案):http : //publications.gbdirect.co.uk/c_book /chapter4/linkage.html
要回答这个问题,“如果一个以上的源文件包含头,则静态是否仅创建一个VAL副本?” ...
没有。VAL将始终在每个包含标头的文件中单独定义。
在这种情况下,C和C ++的标准确实会有所不同。
在C语言中,默认情况下,文件作用域变量为extern。如果您使用的是C,则VAL为静态,而ANOTHER_VAL为extern。
请注意,如果标头包含在不同的文件中(相同的全局名称定义了两次),现代链接器可能会抱怨ANOTHER_VAL,并且如果另一个文件中的ANOTHER_VAL初始化为不同的值,则肯定会抱怨。
在C ++中,如果文件范围变量是const,则默认情况下为静态;如果不是,则默认为extern。如果您使用的是C ++,则VAL和ANOTHER_VAL都是静态的。
您还需要考虑两个变量都指定为const的事实。理想情况下,编译器将始终选择内联这些变量,而不为它们包括任何存储。可以分配存储的原因很多。我能想到的...
您也必须在未定义静态变量的情况下对其进行声明(这是因为存储类修饰符static和extern是互斥的)。可以在头文件中定义静态变量,但是这将导致每个包含头文件的源文件都有其自己的变量私有副本,这可能不是预期的。
const变量在C ++中默认是静态的,但在外部C中是静态的。因此,如果您使用C ++,则没有什么意义。
(7.11.6 C ++ 2003和Apexndix C都有示例)
比较编译/链接源为C和C ++程序的示例:
bruziuz:~/test$ cat a.c
const int b = 22;
int main(){return 0;}
bruziuz:~/test$ cat b.c
const int b=2;
bruziuz:~/test$ gcc -x c -std=c89 a.c b.c
/tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b'
/tmp/ccDSd0V3.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c
bruziuz:~/test$
bruziuz:~/test$ gcc --version | head -n1
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
static
。它表示对程序员正在做什么的意图/意识,并与缺少隐式的其他类型的声明(以及fwiw,C)保持相等static
。就像virtual
最近包含override
在覆盖函数的声明中一样,这不是必需的,而是更多的自文档说明,对于后者,则有助于进行静态分析。
const
仅在标头中的变量上使用的项目g++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)
。结果产生了约150个多重定义的符号(每个包含标题的翻译单元一个)。我认为我们需要一个static
,inline
或者一个匿名/未命名的名称空间来避免外部链接。
const int
并在名称空间范围和全局名称空间中进行了声明。而且它在某种原因编译和遵循规则“的对象声明为常量,而不是明确声明为extern有内部链接。”” ......也许在这个项目包括头为C编译的源代码,其中的规则完全不同。
静态阻止编译器添加多个实例。使用#ifndef保护,这变得不那么重要了,但是假设标头包含在两个单独的库中,并且应用程序已链接,则将包含两个实例。
static
“重要性降低”。甚至同时使用两者,您最终可能会获得多个内部链接的定义,这可能不是故意的。