陣列宣告
宣告一個數組型別的物件。
目錄 |
[編輯] 語法
陣列宣告是任何簡單的宣告,其宣告符具有以下形式
noptr-declarator [ expr (可選)] attr (可選) |
|||||||||
noptr-declarator | - | 任何有效的宣告符,但如果它以* 、& 或&& 開頭,則必須用括號括起來(否則整個宣告符被視為指標宣告符或引用宣告符)。 |
expr | - | 一個整型常量表達式(直到 C++14)一個型別為std::size_t的轉換常量表達式(從 C++14 開始),其計算結果大於零 |
屬性 | - | (從 C++11 開始) 屬性列表 |
形如T a[N];的宣告,將a宣告為一個數組物件,該物件由N個連續分配的T
型別物件組成。陣列的元素編號為0,...,N - 1,並且可以透過下標運算子[]訪問,例如a[0],...,a[N - 1]。
陣列可以由任何基本型別(除了void)、指標、指向成員的指標、類、列舉或具有已知邊界的其他陣列構造(在這種情況下,該陣列稱為多維陣列)。換句話說,只有物件型別(除了未知邊界的陣列型別)才能是陣列型別的元素型別。元素型別不完整的陣列型別也是不完整型別。
可能受約束的(從 C++20 開始) |
(C++11 起) |
沒有引用陣列或函式陣列。
對陣列型別應用cv限定符(透過typedef或模板型別操作)將限定符應用於元素型別,但任何元素型別為cv限定型別的陣列型別都被視為具有相同的cv限定符。
// a and b have the same const-qualified type "array of 5 const char" typedef const char CC; CC a[5] = {}; typedef char CA[5]; const CA b = {};
與new[]-表示式一起使用時,陣列的大小可以為零;這樣的陣列沒有元素。
int* p = new int[0]; // accessing p[0] or *p is undefined delete[] p; // cleanup still required
[編輯] 賦值
陣列型別的物件不能作為一個整體被修改:儘管它們是左值(例如可以獲取陣列的地址),但它們不能出現在賦值運算子的左側。
int a[3] = {1, 2, 3}, b[3] = {4, 5, 6}; int (*p)[3] = &a; // okay: address of a can be taken a = b; // error: a is an array struct { int c[3]; } s1, s2 = {3, 4, 5}; s1 = s2; // okay: implicitly-defined copy assignment operator // can assign data members of array type
[編輯] 陣列到指標的衰變
存在從陣列型別的左值和右值到指標型別的右值的隱式轉換:它構造一個指向陣列第一個元素的指標。當陣列出現在不期望陣列但期望指標的上下文中時,就會使用此轉換。
#include <iostream> #include <iterator> #include <numeric> void g(int (&a)[3]) { std::cout << a[0] << '\n'; } void f(int* p) { std::cout << *p << '\n'; } int main() { int a[3] = {1, 2, 3}; int* p = a; std::cout << sizeof a << '\n' // prints size of array << sizeof p << '\n'; // prints size of a pointer // where arrays are acceptable, but pointers aren't, only arrays may be used g(a); // okay: function takes an array by reference // g(p); // error for (int n : a) // okay: arrays can be used in range-for loops std::cout << n << ' '; // prints elements of the array // for (int n : p) // error // std::cout << n << ' '; std::iota(std::begin(a), std::end(a), 7); // okay: begin and end take arrays // std::iota(std::begin(p), std::end(p), 7); // error // where pointers are acceptable, but arrays aren't, both may be used: f(a); // okay: function takes a pointer f(p); // okay: function takes a pointer std::cout << *a << '\n' // prints the first element << *p << '\n' // same << *(a + 1) << ' ' << a[1] << '\n' // prints the second element << *(p + 1) << ' ' << p[1] << '\n'; // same }
[編輯] 多維陣列
當陣列的元素型別是另一個數組時,該陣列被稱為多維陣列。
// array of 2 arrays of 3 int each int a[2][3] = {{1, 2, 3}, // can be viewed as a 2 × 3 matrix {4, 5, 6}}; // with row-major layout
請注意,當應用陣列到指標的衰變時,多維陣列會轉換為指向其第一個元素的指標(例如,指向其第一行或第一平面的指標):陣列到指標的衰變只應用一次。
int a[2]; // array of 2 int int* p1 = a; // a decays to a pointer to the first element of a int b[2][3]; // array of 2 arrays of 3 int // int** p2 = b; // error: b does not decay to int** int (*p2)[3] = b; // b decays to a pointer to the first 3-element row of b int c[2][3][4]; // array of 2 arrays of 3 arrays of 4 int // int*** p3 = c; // error: c does not decay to int*** int (*p3)[3][4] = c; // c decays to a pointer to the first 3 × 4-element plane of c
[編輯] 未知邊界陣列
如果宣告陣列時省略了expr,則宣告的型別為“T的未知邊界陣列”,這是一種不完整型別,除非在帶有聚合初始化器的宣告中使用。
extern int x[]; // the type of x is "array of unknown bound of int" int a[] = {1, 2, 3}; // the type of a is "array of 3 int"
由於陣列元素不能是未知邊界的陣列,因此多維陣列不能在除第一個維度之外的維度中具有未知邊界。
extern int a[][2]; // okay: array of unknown bound of arrays of 2 int extern int b[2][]; // error: array has incomplete element type
如果在同一作用域中存在該實體的先前宣告,其中已指定邊界,則省略的陣列邊界被視為與該先前宣告中的邊界相同,對於類的靜態資料成員的定義也是如此。
extern int x[10]; struct S { static int y[10]; }; int x[]; // OK: bound is 10 int S::y[]; // OK: bound is 10 void f() { extern int x[]; int i = sizeof(x); // error: incomplete object type }
可以形成指向未知邊界陣列的引用和指標,但不能(直到 C++20)並且可以(從 C++20 開始)用已知邊界陣列和指向已知邊界陣列的指標進行初始化或賦值。請注意,在 C 程式語言中,指向未知邊界陣列的指標與指向已知邊界陣列的指標相容,因此可以在兩個方向上進行轉換和賦值。
extern int a1[]; int (&r1)[] = a1; // okay int (*p1)[] = &a1; // okay int (*q)[2] = &a1; // error (but okay in C) int a2[] = {1, 2, 3}; int (&r2)[] = a2; // okay (since C++20) int (*p2)[] = &a2; // okay (since C++20)
指向未知邊界陣列的指標不能參與指標算術,也不能用在下標運算子的左側,但可以被解引用。
[編輯] 陣列右值
儘管陣列不能透過值從函式返回,也不能作為大多數型別轉換表示式的目標,但可以透過使用類型別名並透過花括號初始化函式式型別轉換來構造陣列臨時量,從而形成陣列純右值。
與類純右值一樣,陣列純右值在求值時透過臨時實體化轉換為將亡值。 |
(C++17 起) |
陣列將亡值可以直接透過訪問類右值的陣列成員,或者透過使用std::move或其他返回右值引用的型別轉換或函式呼叫來形成。
#include <iostream> #include <type_traits> #include <utility> void f(int (&&x)[2][3]) { std::cout << sizeof x << '\n'; } struct X { int i[2][3]; } x; template<typename T> using identity = T; int main() { std::cout << sizeof X().i << '\n'; // size of the array f(X().i); // okay: binds to xvalue // f(x.i); // error: cannot bind to lvalue int a[2][3]; f(std::move(a)); // okay: binds to xvalue using arr_t = int[2][3]; f(arr_t{}); // okay: binds to prvalue f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // okay: binds to prvalue }
輸出
24 24 24 24 24
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 393 | C++98 | 指向未知邊界陣列的指標或引用 不能作為函式引數 |
允許 |
CWG 619 | C++98 | 當省略陣列邊界時,不能 從之前的宣告中推斷出來 |
允許推斷 |
CWG 2099 | C++98 | 即使提供了初始化器,也不能省略 陣列靜態資料成員的邊界 |
允許省略 |
CWG 2397 | C++11 | auto不能用作元素型別 | 允許 |
[編輯] 參閱
C 文件,關於陣列宣告
|