1. 程式人生 > >[C/C++]_[初級]_[關於陣列的初始化問題]

[C/C++]_[初級]_[關於陣列的初始化問題]

場景

1.我們知道棧上建立的POD陣列可以使用 {0}初始化,

<< C++ Primer 3th Edition >> 第3.9部分, 陣列.

如果指定了維數 那麼初始化列表提供的元素的個數不能超過這個值,否則,將導致編譯錯誤.
如果指定的維數大於給出的元素的個數 那麼沒有被顯式初始化的元素將被置為 0.

2.對於C++ new 出來的free store陣列new int[5] 如何進行初始化是一個頭疼的問題. 因為大部分書裡也沒提到, << C++ Primer 3th Edition >> 也只是說 new 出來的陣列需要遍歷每個元素進行初始化. 可以這樣豈不是太麻煩了? C++ 有一個
std::fill_n

來初始化, 但是它應該也是和指標一樣的操作方式, +1遞增和賦值, 它試用於POD陣列或非POD陣列. 可以也需要兩步, 先宣告陣列後呼叫 std::fill_n,有沒有一步過的?答案是有的. 對於POD型別陣列 new int[5](),最後加一個無引數括號即可初始化new POD陣列, 原因可以檢視下邊的說明. 參考裡也有stackoverflow的說明, 但是沒有指出規範裡的哪部分. 所以我找了下規範, 相當難找.

例子

void TestArray()
{
    std::cout << ".........TestArray............" << std
::endl; auto temp = new char[5]; std::fill_n(temp,5,1); for(int i = 0; i< 5;++i){ std::cout << "address: " << (int)&(temp[i]) << std::endl; assert((temp+i) == &temp[i]); } // 動態陣列是無法計算出大小的. 這裡win32指標是4位元組,x64指標是8位元組 int tsize = sizeof
(temp); std::cout << "tsize: " << tsize << std::endl; int size = sizeof(*temp); std::cout << "size: " << size << std::endl; memset(temp,0,size*5); delete[] temp; // 靜態陣列在編譯時已經可以用sizeof計算出陣列所佔位元組數. char temp3[5]={0}; int csize = sizeof(temp3); std::cout << "csize: " << csize << std::endl; for(int i = 0; i< 5;++i){ std::cout << "address: " << (int)&(temp3[i]) << std::endl; assert((temp3+i) == &temp3[i]); } std::cout << "....................." << std::endl; auto temp2 = new std::string[5](); for(int i = 0; i< 5;++i){ std::cout << "address: " << (int)&(temp2[i]) << std::endl; temp2[i] = "ii"; assert((temp2+i) == &temp2[i]); } std::cout << "....................." << std::endl; std::fill_n(temp2,5,"12345\n"); std::for_each(temp2,temp2+5,[](std::string& one){ std::cout << "one: " << one << std::endl; }); std::vector<int> ii; ii.resize(5); std::fill_n(ii.begin(),5,100); std::cout << "....................." << std::endl; std::for_each(ii.begin(),ii.end(),[](int one){ std::cout << "one: " << one << std::endl; }); }

輸出

.........TestArray............
address: 1541000
address: 1541001
address: 1541002
address: 1541003
address: 1541004
tsize: 4
size: 1
csize: 5
address: 2620772
address: 2620773
address: 2620774
address: 2620775
address: 2620776
.....................
address: 1541156
address: 1541188
address: 1541220
address: 1541252
address: 1541284
.....................
one: 12345

one: 12345

one: 12345

one: 12345

one: 12345

.....................
one: 100
one: 100
one: 100
one: 100
one: 100
Press any key to continue . . .

說明

  1. malloc 或者 new 建立的陣列是在 free store上, 也就是我們說的這個區域的物件生命週期結束後, 即不使用這個物件後,
    還是可以通過指向這個物件的地址指標訪問到這個區域, 比如重複使用的記憶體池. 直到顯式呼叫 delete 或 free 這個 free store 才會被釋放.

  2. new 建立的free store陣列或C的棧上的陣列, 都是遵守陣列訪問規則, 即通過 或[]來進行訪問. 比如陣列 a,a[5] = (a+5), 所以對於POD型別的陣列, 是可以用memset來進行賦值的, 因為它們是連續的地址和連續的記憶體空間佈局. 如果是非POD型別, 首先沒呼叫建構函式已經有很大問題.

  3. 一下是關於 new 陣列()初始化的摘錄規範中的說明, 不得不說規範確認難讀懂, 有問題的請指教.

<< The C++ Programming Language 4th Edition >> 第7.3部分. Arrays

void f()
{
    int a2 [20]; // 20 ints on the stack
    int*p = new int[40]; // 40 ints on the free store
    // ...
}

<< ISO/IEC 14882:2011(E)>> 第5.3.4部分 New

new-initializer:
    ( expression-list opt )
    braced-init-list

A new-expression that creates an object of type T initializes that object as follows:
— If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed,
the object has indeterminate value.
— Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-
initialization.

— 如果 new初始化語句被忽略, 那麼這個物件是預設初始化的(8.5); 如果沒有初始化被執行, 那麼這個物件有不確定值.
— 否則, 這個new初始化語句會根據8.5的初始化規則來解析.

<< ISO/IEC 14882:2011(E)>> 第8.5部分 Initializers

10. An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
[Note: Since () is not permitted by the syntax for initializer,
X a();
is not the declaration of an object of class X, but the declaration of a function taking no argument and
returning an X. The form () is permitted in certain other initialization contexts (5.3.4 (New), 5.2.3, 12.6.2).
—end note ]

7. To value-initialize an object of type T means:if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no accessible default
constructor);if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object
is zero-initialized and, if Ts implicitly-declared default constructor is non-trivial, that constructor is
called.
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized.


10. 一個物件如果它的初始化語句是一個空的小括號, (), 那麼它是值初始化的. [注意: 因為()在初始化語句裡不被允許,
 X a(); 不是宣告一個類X的物件, 但是聲明瞭一個不帶引數的函式並返回給X. 格式()在特定初始化上下文的情況下是被允許的.
(5.3.4, 5.2.3, 12.6.2)]

7. 對於型別T物件的值初始化:
-- 如果 T 是一個類型別帶有使用者自定義的建構函式, 那麼這個預設函式會被呼叫. 
-- 如果 T 是一個非聯合類型別並且沒有使用者自定義的建構函式, 那麼這個物件會被 zero-initialized.(這裡就是初始化為0了)
-- 如果 T 是一個數組型別, 那麼每個元素都會值初始化.
-- 否則 這個物件會被 zero-initialized. 

術語

<< ISO/IEC 14882:2011(E)>> 第9部分 Class.

POD: plain old data. A POD struct is a non-union class that is both a trivial class and a standard-layout class, 
and has no non-static data members of type non-POD struct, non-POD union (or array of such types)

扁平原始資料. 一個POD結構是一個非聯合類, 意思是它既是一個 小類 又是一個標準佈局類, 同時沒有非POD結構或非POD聯合作為資料成員.
standard-layout struct: A standard-layout struct is a standard-layout class defined with the class-key struct or
the class-key class. A standard-layout union is a standard-layout class defined with the class-key union.

標準佈局結構體是一個標準佈局類,它使用類關鍵字 struct或者 class定義. 一個標準佈局聯合體也是一個標準佈局類,
但是用類關鍵字 union定義.
trivial class: A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable.
[Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base
classes.—end note ]

參考