Share via


陣列 (C++)

陣列是相同類型的 物件序列,其佔用連續記憶體區域。 傳統的 C 樣式陣列是許多 Bug 的來源,但仍很常見,特別是在較舊的程式碼基底中。 在新式 C++ 中,我們強烈建議使用 std::vectorstd::array 而不是本節中所述的 C 樣式陣列。 這兩種標準程式庫類型都會將其元素儲存為連續的記憶體區塊。 不過,它們提供更大的型別安全性,並支援保證指向序列內有效位置的反覆運算器。 如需詳細資訊,請參閱 容器

堆疊宣告

在 C++ 陣列宣告中,陣列大小是在變數名稱之後指定,而不是在類型名稱之後指定,如同某些其他語言。 下列範例會宣告堆疊上要配置的 1000 雙精度浮點數陣列。 元素數目必須以整數常值形式提供,否則為常數運算式。 這是因為編譯器必須知道要配置多少堆疊空間;它無法使用在執行時間計算的值。 陣列中的每個元素都會被指派預設值 0。 如果您未指派預設值,每個元素一開始都會包含該記憶體位置發生的任何隨機值。

    constexpr size_t size = 1000;

    // Declare an array of doubles to be allocated on the stack
    double numbers[size] {0};

    // Assign a new value to the first element
    numbers[0] = 1;

    // Assign a value to each subsequent element
    // (numbers[1] is the second element in the array.)
    for (size_t i = 1; i < size; i++)
    {
        numbers[i] = numbers[i-1] * 1.1;
    }

    // Access each element
    for (size_t i = 0; i < size; i++)
    {
        std::cout << numbers[i] << " ";
    }

陣列中的第一個專案是第零個元素。 最後一個專案是 ( n-1) 元素,其中 n 是陣列可以包含的專案數目。 宣告中的專案數目必須是整數型別,而且必須大於 0。 您必須負責確保程式永遠不會將值傳遞至大於 (size - 1) 的下標運算子。

只有陣列是 或 union 中的最後一個 struct 欄位,以及啟用 Microsoft 延伸模組時( /Za/permissive- 未設定)時,零大小的陣列才合法。

堆疊型陣列的配置和存取速度比堆積型陣列更快。 不過,堆疊空間有限。 陣列元素的數目不能太大,因此會耗用太多堆疊記憶體。 太多取決於您的程式。 您可以流量分析工具來判斷陣列是否太大。

堆積宣告

您可能需要在堆疊上配置太大的陣列,或在編譯時期不知道其大小。 您可以使用運算式,在堆積 new[] 上配置這個陣列。 運算子會傳回第一個專案的指標。 下標運算子在指標變數上的運作方式與在堆疊型陣列上的運作方式相同。 您也可以使用 指標算術 ,將指標移至陣列中的任何任意專案。 您必須負責確保:

  • 您一律會保留原始指標位址的複本,以便在不再需要陣列時刪除記憶體。
  • 您不會遞增或遞減超過陣列界限的指標位址。

下列範例示範如何在執行時間定義堆積上的陣列。 它示範如何使用下標運算子以及使用指標算術來存取陣列元素:

void do_something(size_t size)
{
    // Declare an array of doubles to be allocated on the heap
    double* numbers = new double[size]{ 0 };

    // Assign a new value to the first element
    numbers[0] = 1;

    // Assign a value to each subsequent element
    // (numbers[1] is the second element in the array.)
    for (size_t i = 1; i < size; i++)
    {
        numbers[i] = numbers[i - 1] * 1.1;
    }

    // Access each element with subscript operator
    for (size_t i = 0; i < size; i++)
    {
        std::cout << numbers[i] << " ";
    }

    // Access each element with pointer arithmetic
    // Use a copy of the pointer for iterating
    double* p = numbers;

    for (size_t i = 0; i < size; i++)
    {
        // Dereference the pointer, then increment it
        std::cout << *p++ << " ";
    }

    // Alternate method:
    // Reset p to numbers[0]:
    p = numbers;

    // Use address of pointer to compute bounds.
    // The compiler computes size as the number
    // of elements * (bytes per element).
    while (p < (numbers + size))
    {
        // Dereference the pointer, then increment it
        std::cout << *p++ << " ";
    }

    delete[] numbers; // don't forget to do this!

}
int main()
{
    do_something(108);
}

初始化陣列

您可以在迴圈、一次一個專案或單一語句中初始化陣列。 下列兩個數組的內容完全相同:

    int a[10];
    for (int i = 0; i < 10; ++i)
    {
        a[i] = i + 1;
    }

    int b[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

將陣列傳遞至函式

當陣列傳遞至函式時,它會當做第一個專案的指標傳遞,無論是堆疊型或堆積型陣列。 指標不包含其他大小或類型資訊。 此行為稱為 指標衰變 。 當您將陣列傳遞至函式時,必須一律在個別參數中指定元素數目。 此行為也表示當陣列傳遞至函式時,不會複製陣列元素。 若要防止函式修改專案,請將 參數指定為專案的指標 const

下列範例顯示接受陣列和長度的函式。 指標指向原始陣列,而非複本。 因為 參數不是 const ,函式可以修改陣列元素。

void process(double *p, const size_t len)
{
    std::cout << "process:\n";
    for (size_t i = 0; i < len; ++i)
    {
        // do something with p[i]
    }
}

將陣列參數 pconst 宣告並定義為 ,使其在函式區塊中為唯讀:

void process(const double *p, const size_t len);

同一個函式也可以透過這些方式宣告,而且行為沒有變更。 陣列仍會當做第一個專案的指標傳遞:

// Unsized array
void process(const double p[], const size_t len);

// Fixed-size array. Length must still be specified explicitly.
void process(const double p[1000], const size_t len);

多維陣列

從其他陣列建構的陣列是多維陣列。 這些多維陣列是藉由依序放置多個包含括號的常數運算式所指定。 例如,以下列宣告為例:

int i2[5][7];

它會指定類型的 int 陣列,在概念上排列在五列和七個數據行的二維矩陣中,如下圖所示:

Conceptual layout of a multidimensional array.

影像是寬 7 個儲存格的格線,高度為 5 個儲存格。 每個儲存格都包含儲存格的索引。 第一個儲存格索引標示為 0,0。 該資料列中的下一個儲存格是 0,1,依此資料列的最後一個儲存格為 0,6。 下一個資料列會從索引 1,0 開始。 之後的儲存格索引為 1,1。 該資料列中的最後一個儲存格是 1,6。 此模式會重複到最後一個資料列,其開頭為索引 4,0。 最後一個資料列中的最後一個儲存格索引為 4,6。 :::image-end

您可以宣告具有初始化運算式清單的多維度陣列(如初始化運算式 中所述 )。 在這些宣告中,可以省略指定第一個維度界限的常數運算式。 例如:

// arrays2.cpp
// compile with: /c
const int cMarkets = 4;
// Declare a float that represents the transportation costs.
double TransportCosts[][cMarkets] = {
   { 32.19, 47.29, 31.99, 19.11 },
   { 11.29, 22.49, 33.47, 17.29 },
   { 41.97, 22.09,  9.76, 22.55 }
};

上述宣告會定義一個三列四行的陣列。 資料列代表工廠,資料行則代表工廠出貨的市場。 值是從工廠到市場的運費。 陣列的第一個維度會被省略,但編譯器會藉由檢查初始設定式填入該維度。

在 n 維度陣列類型上使用間接運算子 • 會產生 n-1 維度陣列。 如果 n 為 1,則會產生純量 (或 array 元素)。

C++ 陣列是以列為主要順序進行儲存。 以列為主要順序表示最後一個註標變更最快速。

範例

您也可以省略函數宣告中多維度陣列第一個維度的界限規格,如下所示:

// multidimensional_arrays.cpp
// compile with: /EHsc
// arguments: 3
#include <limits>   // Includes DBL_MAX
#include <iostream>

const int cMkts = 4, cFacts = 2;

// Declare a float that represents the transportation costs
double TransportCosts[][cMkts] = {
   { 32.19, 47.29, 31.99, 19.11 },
   { 11.29, 22.49, 33.47, 17.29 },
   { 41.97, 22.09,  9.76, 22.55 }
};

// Calculate size of unspecified dimension
const int cFactories = sizeof TransportCosts /
                  sizeof( double[cMkts] );

double FindMinToMkt( int Mkt, double myTransportCosts[][cMkts], int mycFacts);

using namespace std;

int main( int argc, char *argv[] ) {
   double MinCost;

   if (argv[1] == 0) {
      cout << "You must specify the number of markets." << endl;
      exit(0);
   }
   MinCost = FindMinToMkt( *argv[1] - '0', TransportCosts, cFacts);
   cout << "The minimum cost to Market " << argv[1] << " is: "
       << MinCost << "\n";
}

double FindMinToMkt(int Mkt, double myTransportCosts[][cMkts], int mycFacts) {
   double MinCost = DBL_MAX;

   for( size_t i = 0; i < cFacts; ++i )
      MinCost = (MinCost < TransportCosts[i][Mkt]) ?
         MinCost : TransportCosts[i][Mkt];

   return MinCost;
}
The minimum cost to Market 3 is: 17.29

FindMinToMkt函式的撰寫方式是新增處理站不需要任何程式碼變更,只需重新編譯。

初始化陣列

具有類別建構函式的物件陣列是由建構函式初始化。 當初始化運算式清單中的專案比陣列中的專案少時,預設建構函式會用於其餘元素。 如果未定義類別的預設建構函式,則必須 完成 初始化運算式清單,也就是說,陣列中的每個元素都必須有一個初始化運算式。

以定義兩個建構函式的 Point 類別為例:

// initializing_arrays1.cpp
class Point
{
public:
   Point()   // Default constructor.
   {
   }
   Point( int, int )   // Construct from two ints
   {
   }
};

// An array of Point objects can be declared as follows:
Point aPoint[3] = {
   Point( 3, 3 )     // Use int, int constructor.
};

int main()
{
}

aPoint 的第一個元素是使用 Point( int, int ) 建構函式建構,其餘兩個元素則是使用預設建構函式建構。

靜態成員陣列(不論是否 const )可以在其定義中初始化(在類別宣告外部)。 例如:

// initializing_arrays2.cpp
class WindowColors
{
public:
    static const char *rgszWindowPartList[7];
};

const char *WindowColors::rgszWindowPartList[7] = {
    "Active Title Bar", "Inactive Title Bar", "Title Bar Text",
    "Menu Bar", "Menu Bar Text", "Window Background", "Frame"   };
int main()
{
}

存取陣列元素

使用陣列註標運算子 ([ ]),您可以存取陣列的個別項目。 如果您使用沒有下標的一維陣列名稱稱,則會評估為數組第一個專案的指標。

// using_arrays.cpp
int main() {
   char chArray[10];
   char *pch = chArray;   // Evaluates to a pointer to the first element.
   char   ch = chArray[0];   // Evaluates to the value of the first element.
   ch = chArray[3];   // Evaluates to the value of the fourth element.
}

當您使用多維陣列時,您可以在運算式中使用各種組合。

// using_arrays_2.cpp
// compile with: /EHsc /W1
#include <iostream>
using namespace std;
int main() {
   double multi[4][4][3];   // Declare the array.
   double (*p2multi)[3];
   double (*p1multi);

   cout << multi[3][2][2] << "\n";   // C4700 Use three subscripts.
   p2multi = multi[3];               // Make p2multi point to
                                     // fourth "plane" of multi.
   p1multi = multi[3][2];            // Make p1multi point to
                                     // fourth plane, third row
                                     // of multi.
}

在上述程式碼中, multi 是 類型的 double 三維陣列。 指標 p2multi 指向大小為三的 double 陣列。 在此範例中,陣列搭配使用的註標有一個、兩個和三個。 雖然指定所有注標比較常見,如 語句所示 cout ,但有時選取陣列元素的特定子集會很有用,如下列 cout 語句所示。

多載下標運算子

與其他運算子一樣,下標運算子 ( [] ) 可由使用者重新定義。 註標運算子的預設行為 (如果未多載) 是使用下列方法結合陣列名稱和註標:

*((array_name) + (subscript))

如同所有牽涉到指標類型的新增專案,調整會自動調整類型的大小。 結果值不是 來源的 n 個位元組, 而是陣列的第 n array_name 元素。 如需此轉換的詳細資訊,請參閱 加法運算子

同樣地,對於多維陣列而言,位址是使用下列方法衍生:

((array_name) + (subscript1 * max2 * max3 * ... * maxn) + (subscript2 * max3 * ... * maxn) + ... + subscriptn))

運算式中的陣列

當陣列類型的識別碼出現在 、address-of ( & ) 或參考初始化以外的 sizeof 運算式時,它會轉換成第一個陣列專案的指標。 例如:

char szError1[] = "Error: Disk drive not ready.";
char *psz = szError1;

指標 psz 會指向陣列 szError1 的第一個元素。 陣列與指標不同,無法修改 l 值。 這就是為什麼下列指派不合法的原因:

szError1 = psz;

另請參閱

std::array