※ 引述《justhere (发废文就是生活的小确幸)》之铭言:
: 各位好,小弟新手,
: 目前在看C++ primier fifth edition,
: 进度到variable的定义与宣告这个小节,
: 在讲extern 这个keyword时书中举一个小范例:
: extern int i; // declares but does not define i
: int j; // declares and defines i
: 他解释
: To obtain a declaration that is not also a definition,
: we add extern keyword and may not provide an explicit initializer
: 请问具体来说c++中initializer所做的事情是什么呢?
: 是分配内存位置和值给该name吗?
: 感谢
先不管 extern, 在语法上 int i; 这样写就是宣告. 而对于非函式
的宣告来说, 即使我们可以不写 initializer, 但物件一定得经过
初始化, 而依照这个物件被宣告的地方, 会有不同的初始化行为,
有 static storage 如全域 int 物件, 就会经过 zero-initializ-
ation (效果等同于 = 0, 和 memset(&obj, 0, sizeof(obj)) 意义
不同), 如果是区域 int 物件则不给初始值; 但无论给不给初始值,
物件被宣告的当下, 它的实体即存在于该 TU (translation unit)
里 (这个结果才是书中说的定义)
在宣告前方加上 extern 修饰符语意就变了, 这时不为了创造物件
实体, 而是想在当下能重复使用某处宣告过的物件 (可以存在同个
TU 或别的 TU), 而跨 TU 存取的情况下, 这个物件必须要具备
external linkage 才行. 考虑以下程式码, 请问印出的 i 值是多
少?
// translation unit #1
static int i = 1;
namespace ns {
int i = 2;
}
// translation unit #2
void foo() {
extern int i; // declare i but i is not defined in foo()
std::cout << "i = " << i << std::endl;
}
以上是陷阱题, 在 TU #2 里面虽然预期会有个 i 宣告在全域底下
, 结果因为在 TU #1 里全域的 i 宣告有加上 static 修饰符所以
无法在别的 TU 里使用. 这段程式码会造成连结错误.
加了 extern 的宣告还是有办法回复原本宣告的功能, 只要明确地
用 initializer 初始化即可; 但是这样写语意是矛盾的, 通常编译
器会给警告 (这时候会忽略 extern 修饰符).
除了函式会特别把宣告还有建立实体分成两种写法, non-inline
static data member 也是如此:
class foo {
static int i; // declare here
};
int foo::i = 1; // define here
这时候我们不会说 static int i; 是定义: 它还是宣告, 但真正建
立实体的动作必须从 class definition 拉出来写在命名空间内.
最后来总结几个重点:
1. 宣告或定义要看情境, 不是单纯用语法来分辨
2. 加 extern 修饰符也可定义物件
3. extern 是用来存取具有 external linkage 的物件 (跨 TU)