指標宣告
宣告指標或指向成員型別的變數。
目錄 |
[編輯] 語法
指標宣告是任何簡單宣告,其宣告符具有以下形式
* attr (可選) cv (可選) declarator |
(1) | ||||||||
nested-name-specifier * attr (可選) cv (可選) declarator |
(2) | ||||||||
C
的非靜態成員的指標,其型別由宣告說明符序列S
確定。巢狀名稱說明符 | - | 一系列名稱和作用域解析運算子:: |
屬性 | - | (C++11 起) 屬性列表 |
cv | - | 應用於正在宣告的指標(而不是指向的型別,其限定符是宣告說明符序列的一部分)的const/volatile限定 |
宣告符 | - | 除引用宣告符(沒有指向引用的指標)之外的任何宣告符。它可以是另一個指標宣告符(允許指向指標的指標) |
沒有指向引用的指標,也沒有指向位域的指標。通常,“指標”的提及沒有詳細說明,不包括指向(非靜態)成員的指標。
[編輯] 指標
每個指標型別的值都是以下之一
指向物件的指標表示物件佔用的記憶體中第一個位元組的地址。指向物件末尾之後的指標表示物件佔用的儲存空間末尾之後的記憶體中第一個位元組的地址。
請注意,表示相同地址的兩個指標可能仍然具有不同的值。
struct C { int x, y; } c; int* px = &c.x; // value of px is "pointer to c.x" int* pxe= px + 1; // value of pxe is "pointer past the end of c.x" int* py = &c.y; // value of py is "pointer to c.y" assert(pxe == py); // == tests if two pointers represent the same address // may or may not fire *pxe = 1; // undefined behavior even if the assertion does not fire
透過無效指標值進行間接操作以及將無效指標值傳遞給解除分配函式會導致未定義行為。任何其他使用無效指標值的行為都是實現定義的。某些實現可能會定義複製無效指標值會導致系統生成的執行時故障。
[編輯] 指向物件的指標
指向物件的指標可以用應用於任何物件型別表示式(包括其他指標型別)的取址運算子的返回值進行初始化
int n; int* np = &n; // pointer to int int* const* npp = &np; // non-const pointer to const pointer to non-const int int a[2]; int (*ap)[2] = &a; // pointer to array of int struct S { int n; }; S s = {1}; int* sp = &s.n; // pointer to the int that is a member of s
指標可以作為內建間接運算子(一元operator*)的操作數出現,該運算子返回標識所指向物件的左值表示式
int n; int* p = &n; // pointer to n int& r = *p; // reference is bound to the lvalue expression that identifies n r = 7; // stores the int 7 in n std::cout << *p; // lvalue-to-rvalue implicit conversion reads the value from n
指向類物件的指標也可以作為成員訪問運算子operator->
和operator->*
的左操作數出現。
由於陣列到指標的隱式轉換,指向陣列第一個元素的指標可以用陣列型別的表示式進行初始化
int a[2]; int* p1 = a; // pointer to the first element a[0] (an int) of the array a int b[6][3][8]; int (*p2)[3][8] = b; // pointer to the first element b[0] of the array b, // which is an array of 3 arrays of 8 ints
由於指標的派生到基類隱式轉換,指向基類的指標可以用派生類物件的地址進行初始化
struct Base {}; struct Derived : Base {}; Derived d; Base* p = &d;
如果Derived
是多型的,則此類指標可用於進行虛擬函式呼叫。
某些加法、減法、遞增和遞減運算子定義用於指向陣列元素的指標:此類指標滿足LegacyRandomAccessIterator要求,並允許C++庫演算法與原始陣列一起使用。
比較運算子在某些情況下定義用於指向物件的指標:表示相同地址的兩個指標比較相等,兩個空指標值比較相等,指向同一陣列元素的指標與這些元素的陣列索引比較相同,以及具有相同成員訪問許可權的非靜態資料成員的指標按這些成員的宣告順序進行比較。
許多實現還提供對隨機來源指標的嚴格全序,例如,如果它們在連續虛擬地址空間中實現為地址。那些不提供(例如,指標並非所有位都是記憶體地址的一部分,必須忽略進行比較,或者需要額外計算,或者指標和整數之間不是一對一關係)的實現,則提供std::less的特化以保證此特性。這使得可以將所有隨機來源的指標用作標準關聯容器(如std::set或std::map)中的鍵。
[編輯] 指向void的指標
指向任何型別的物件的指標可以隱式轉換為指向(可能是cv-限定的)void的指標;指標值不變。反向轉換,需要static_cast
或顯式轉換,得到原始指標值
int n = 1; int* p1 = &n; void* pv = p1; int* p2 = static_cast<int*>(pv); std::cout << *p2 << '\n'; // prints 1
如果原始指標指向某個多型型別物件內的基類子物件,則可以使用dynamic_cast
獲取指向最派生型別的完整物件的void*。
指向void的指標與指向char的指標具有相同的大小、表示和對齊方式。
指向void的指標用於傳遞未知型別的物件,這在C介面中很常見:std::malloc返回void*,std::qsort期望使用者提供的回撥接受兩個const void*引數。pthread_create
期望使用者提供的回撥接受並返回void*。在所有情況下,呼叫者有責任在使用前將指標轉換為正確的型別。
[編輯] 指向函式的指標
指向函式的指標可以用非成員函式或靜態成員函式的地址進行初始化。由於函式到指標的隱式轉換,取址運算子是可選的
void f(int); void (*p1)(int) = &f; void (*p2)(int) = f; // same as &f
與函式或函式引用不同,函式指標是物件,因此可以儲存在陣列中,可以複製、賦值等。
void (a[10])(int); // Error: array of functions void (&a[10])(int); // Error: array of references void (*a[10])(int); // OK: array of pointers to functions
注意:涉及函式指標的宣告通常可以使用類型別名簡化
using F = void(int); // named type alias to simplify declarations F a[10]; // Error: array of functions F& a[10]; // Error: array of references F* a[10]; // OK: array of pointers to functions
函式指標可以用作函式呼叫運算子的左運算元,這會呼叫所指向的函式
int f(int n) { std::cout << n << '\n'; return n * n; } int main() { int (*p)(int) = f; int x = p(7); }
解引用函式指標會得到標識所指向函式的左值
int f(); int (*p)() = f; // pointer p is pointing to f int (&r)() = *p; // the lvalue that identifies f is bound to a reference r(); // function f invoked through lvalue reference (*p)(); // function f invoked through the function lvalue p(); // function f invoked directly through the pointer
函式指標可以從過載集中初始化,過載集可以包括函式、函式模板特化和函式模板,如果只有一個過載與指標型別匹配(有關詳細資訊,請參見過載函式的地址)
template<typename T> T f(T n) { return n; } double f(double n) { return n; } int main() { int (*p)(int) = f; // instantiates and selects f<int> }
相等比較運算子定義用於函式指標(如果指向同一函式,則它們比較相等)。
[編輯] 指向成員的指標
[編輯] 指向資料成員的指標
指向非靜態成員物件m
的指標,它是類C
的成員,可以用表示式&C::m精確初始化。諸如&(C::m)或C
成員函式內的&m等表示式不會形成指向成員的指標。
此類指標可以用作指向成員的訪問運算子operator.*和operator->*的右運算元
指向可訪問的、非模糊的、非虛基類的資料成員的指標可以隱式轉換為指向派生類中相同資料成員的指標
struct Base { int m; }; struct Derived : Base {}; int main() { int Base::* bp = &Base::m; int Derived::* dp = bp; Derived d; d.m = 1; std::cout << d.*dp << ' ' << d.*bp << '\n'; // prints 1 1 }
反向轉換,即從指向派生類資料成員的指標到指向非模糊的非虛基類資料成員的指標,允許使用static_cast
和顯式轉換,即使基類沒有該成員(但在指標用於訪問時,最派生類有)
指向成員的指標所指向的型別本身可以是指向成員的指標:指向成員的指標可以是多級,並且可以在每個級別上具有不同的cv限定。指標和指向成員的混合多級組合也是允許的
struct A { int m; // const pointer to non-const member int A::* const p; }; int main() { // non-const pointer to data member which is a const pointer to non-const member int A::* const A::* p1 = &A::p; const A a = {1, &A::m}; std::cout << a.*(a.*p1) << '\n'; // prints 1 // regular non-const pointer to a const pointer-to-member int A::* const* p2 = &a.p; std::cout << a.**p2 << '\n'; // prints 1 }
[編輯] 指向成員函式的指標
指向類C
的非靜態成員函式f的指標可以用表示式&C::f精確初始化。諸如&(C::f)或C
成員函式內的&f等表示式不會形成指向成員函式的指標。
此類指標可以用作指向成員訪問運算子operator.*和operator->*的右運算元。所得到的表示式只能用作函式呼叫運算子的左運算元
struct C { void f(int n) { std::cout << n << '\n'; } }; int main() { void (C::* p)(int) = &C::f; // pointer to member function f of class C C c; (c.*p)(1); // prints 1 C* cp = &c; (cp->*p)(2); // prints 2 }
指向基類成員函式的指標可以隱式轉換為指向派生類中相同成員函式的指標
struct Base { void f(int n) { std::cout << n << '\n'; } }; struct Derived : Base {}; int main() { void (Base::* bp)(int) = &Base::f; void (Derived::* dp)(int) = bp; Derived d; (d.*dp)(1); (d.*bp)(2); }
反向轉換,即從指向派生類成員函式的指標到指向非模糊的非虛基類成員函式的指標,允許使用static_cast
和顯式轉換,即使基類沒有該成員函式(但在指標用於訪問時,最派生類有)
struct Base {}; struct Derived : Base { void f(int n) { std::cout << n << '\n'; } }; int main() { void (Derived::* dp)(int) = &Derived::f; void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp); Derived d; (d.*bp)(1); // okay: prints 1 Base b; (b.*bp)(2); // undefined behavior }
指向成員函式的指標可以用作回撥或函式物件,通常在應用std::mem_fn或std::bind之後
#include <algorithm> #include <cstddef> #include <functional> #include <iostream> #include <string> int main() { std::vector<std::string> v = {"a", "ab", "abc"}; std::vector<std::size_t> l; transform(v.begin(), v.end(), std::back_inserter(l), std::mem_fn(&std::string::size)); for (std::size_t n : l) std::cout << n << ' '; std::cout << '\n'; }
輸出
1 2 3
[編輯] 空指標
每種型別的指標都有一個特殊值,稱為該型別的空指標值。值為 null 的指標不指向物件或函式(解引用空指標的行為是未定義的),並且與所有相同型別的、其值也為null的指標比較相等。
空指標常量可用於將指標初始化為 null 或將 null 值賦給現有指標,它是以下值之一
- 值為零的整數文字。
|
(C++11 起) |
也可以使用宏NULL,它擴充套件為實現定義的空指標常量。
空指標可用於指示物件的缺失(例如std::function::target()),或作為其他錯誤條件指示符(例如dynamic_cast)。通常,接收指標引數的函式幾乎總是需要檢查值是否為 null 並以不同方式處理該情況(例如,當傳遞空指標時,delete 表示式不執行任何操作)。
[編輯] 無效指標
如果滿足以下任一條件,則指標值p在評估e的上下文中是有效的
- p是空指標值。
- p是指向函式的指標。
- p是指向物件o或其末尾之後的指標,並且e在o的儲存區域的持續時間內。
如果在評估e中使用指標值p,並且p在e的上下文中無效,則
int* f() { int obj; int* local_ptr = new (&obj) int; *local_ptr = 1; // OK, the evaluation “*local_ptr” is // in the storage duration of “obj” return local_ptr; } int* ptr = f(); // the storage duration of “obj” is expired, // therefore “ptr” is an invalid pointer in the following contexts int* copy = ptr; // implementation-defined behavior *ptr = 2; // undefined behavior: indirection of an invalid pointer delete ptr; // undefined behavior: deallocating storage from an invalid pointer
[編輯] 常量性
- 如果cv在指標宣告中出現在
*
之前,它是宣告說明符序列的一部分,並應用於指向的物件。 - 如果cv在指標宣告中出現在
*
之後,它是宣告符的一部分,並應用於正在宣告的指標。
語法 | 含義 |
---|---|
const T* | 指向常量物件的指標 |
T const* | 指向常量物件的指標 |
T* const | 指向物件的常量指標 |
const T* const | 指向常量物件的常量指標 |
T const* const | 指向常量物件的常量指標 |
// pc is a non-const pointer to const int // cpc is a const pointer to const int // ppc is a non-const pointer to non-const pointer to const int const int ci = 10, *pc = &ci, *const cpc = pc, **ppc; // p is a non-const pointer to non-const int // cp is a const pointer to non-const int int i, *p, *const cp = &i; i = ci; // okay: value of const int copied into non-const int *cp = ci; // okay: non-const int (pointed-to by const pointer) can be changed pc++; // okay: non-const pointer (to const int) can be changed pc = cpc; // okay: non-const pointer (to const int) can be changed pc = p; // okay: non-const pointer (to const int) can be changed ppc = &pc; // okay: address of pointer to const int is pointer to pointer to const int ci = 1; // error: const int cannot be changed ci++; // error: const int cannot be changed *pc = 2; // error: pointed-to const int cannot be changed cp = &ci; // error: const pointer (to non-const int) cannot be changed cpc++; // error: const pointer (to const int) cannot be changed p = pc; // error: pointer to non-const int cannot point to const int ppc = &p; // error: pointer to pointer to const int cannot point to // pointer to non-const int
通常,從一個多級指標到另一個多級指標的隱式轉換遵循限定符轉換中描述的規則。
[編輯] 複合指標型別
當比較運算子的運算元或條件運算子的第二和第三運算元是指標或指向成員的指標時,複合指標型別被確定為這些運算元的通用型別。
給定兩個運算元p1和p2,分別具有型別T1
和T2
,p1和p2只有在滿足以下任一條件時才能具有複合指標型別
|
(直到C++14) | ||
|
(C++14 起) |
p1和p2的複合指標型別C
確定如下
|
(C++11 前) |
|
(C++11 起) |
- 否則,如果滿足以下所有條件
-
T1
或T2
是“指向cv1 void的指標”。 - 另一個型別是“指向cv2
T
的指標”,其中T
是物件型別或void。
-
-
C
是“指向cv12 void的指標”,其中cv12是cv1和cv2的並集。
|
(C++17 起) |
- 否則,如果滿足以下所有條件
-
T1
是“指向C1
的指標”。 -
T2
是“指向C2
的指標”。 C1
和C2
中有一個與另一個引用相關。
-
-
C
是- 如果
C1
與C2
引用相關,則為T1
和T2
的限定符組合型別,或 - 如果
C2
與C1
引用相關,則為T2
和T1
的限定符組合型別。
- 如果
|
(C++17 起) |
- 否則,如果滿足以下所有條件
-
T1
是“指向C1
的非函式型別M1
的成員的指標”。 -
T2
是“指向C2
的非函式型別M2
的成員的指標” -
M1
和M2
除了頂層cv限定符外相同。 C1
和C2
中有一個與另一個引用相關。
-
-
C
是- 如果
C1
與C2
引用相關,則為T2
和T1
的限定符組合型別,或 - 如果
C2
與C1
引用相關,則為T1
和T2
的限定符組合型別。
- 如果
- 否則,如果
T1
和T2
是相似型別,則C
是T1
和T2
的限定符組合型別。 - 否則,p1和p2沒有複合指標型別,需要確定
C
此類型別的程式是格式錯誤的。
using p = void*; using q = const int*; // The determination of the composite pointer type of “p” and “q” // falls into the [“pointer to cv1 void” and “pointer to cv2 T”] case: // cv1 = empty, cv2 = const, cv12 = const // substitute “cv12 = const” into “pointer to cv12 void”: // the composite pointer type is “const void*” using pi = int**; using pci = const int**; // The determination of the composite pointer type of “pi” and “pci” // falls into the [pointers to similar types “C1” and “C2”] case: // C1 = int*, C2 = const int* // they are reference-related types (in both direction) because they are similar // the composite pointer type is the qualification-combined type // of “p1” and “pc1” (or that of “pci” and “pi”): “const void* const *”
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 73 | C++98 | 指向物件的指標從不相等 指向陣列末尾之後的指標 |
對於非空和非函式指標, 比較它們表示的地址 |
CWG 903 | C++98 | 任何求值為 0 的整型常量表達式 曾是空指標常量 |
僅限於值為 0 的整數 文字 |
CWG 1438 | C++98 | 以任何方式使用無效指標值的行為 曾是未定義的 |
除間接引用和傳遞給解除分配函式之外的行為 是實現定義的 是實現定義的 |
CWG 1512 (N3624) |
C++98 | 複合指標型別的規則不完整,因此 不允許int**與const int**進行比較 |
已完成 |
CWG 2206 | C++98 | 指向void的指標和指向 函式的指標具有複合指標型別 |
它們不具有此類型別 |
CWG 2381 | C++17 | 在確定複合指標型別時 不允許函式指標轉換 |
允許 |
CWG 2822 | C++98 | 到達儲存區域持續時間的末尾 可能會使指標值失效 |
指標有效性基於 評估上下文 |
CWG 2933 | C++98 | 指向函式的指標總是無效的 | 它們總是有效的 |
[編輯] 另請參閱
C 文件的指標宣告
|