2015-10-19 48 views
2

我想我知道如何在C++處理內存管理,但這讓我感到困惑:陣列上堆結構不正確初始化

考慮下面的代碼:

struct A { 
    int i; 
}; 

int main(int argc, char* argv[]) { 
    A a{ 5 }; //Constructs an A object on the stack 
    A* b = new A{ 7 }; //Constructs an A object on the heap and stores a pointer to it in b 
    A* c = new A[] { //Construct an array of A objects on the heap and stores a pointer to it in c 
     { 3 }, 
     { 4 }, 
     { 5 }, 
     { 6 } 
    }; 
    std::cout << "a: " << a.i << "\n"; //Prints 'a: 5' 
    std::cout << "b: " << b->i << "\n"; //Prints 'b: 7' 
    std::cout << "c: " << c[0].i << "; " << c[1].i << "; " << c[2].i << "; " << c[3].i << "\n"; 
    //Prints 'c: -33686019; -1414812757; -1414812757; -1414812757' 

    delete b; 
    delete[] c; 
    return 0; 
} 

我不明白爲什麼c的最後打印輸出了那些奇怪的數字。如果我添加一個構造函數一個像這樣:

struct A { 
    A(int i) : i{i} {} 
    int i; 
}; 

然後最後打印出的輸出變爲:

'c: 3; 4; 5; 6' 

理所應當。但現在delete[] c;會給我一個運行時錯誤(不是一個例外),說MyGame.exe has triggered a breakpoint.(我在VS2013工作)。

此外,如果我將行A* c = new A[] {更改爲A* c = new A[4] {,則錯誤消失,並且所有內容都按預期工作。

所以我的問題是: 爲什麼奇怪的數字?如果我沒有定義構造函數,數組中的A對象會不會得到正確的構造? 爲什麼我需要明確指定數組的大小,即使它會編譯和鏈接就好了沒有?以這種方式初始化堆棧上的數組不會給我一個運行時錯誤(我測試過它是肯定的)。

+1

如果你要在堆上分配數組,你需要指定數組的大小。你分配的是一個指針(例如'A *')而不是一個數組。從它讀取調用未定義的行爲。 –

+1

[這裏](http://ideone.com/hZA0iM)它甚至不能編譯! –

+0

奇怪的數字是因爲你的編譯器有點壞。 g ++ 5.1表示「錯誤:預期的初級表達式'之前']'標記'和'錯誤:'A [1]'的初始化程序太多了。 VS 2015說「錯誤C3078:數組大小必須在新表達式中指定」。 VS2013對該代碼做了什麼不清楚。 – molbdnilo

回答

1

Why the weird numbers?

因爲沒有分配內存來支持它們。指針指向Crom知道什麼。該結構不應該編譯。

Won't the A objects in the array get properly constructed somehow if I don't define a constructor?

沒有構造函數,所有的成員將被初始化爲默認值。 int的和大多數普通舊數據類型沒有定義的默認值。在一個典型的實現中,他們得到已經在分配的內存塊中發生的任何值。如果成員對象的類型不是默認構造函數,並且無法創建,那麼會出現編譯器錯誤。

And why do I need to specify the array size explicitly even though it will compile and link just fine without?

它不被編譯,陣列的大小(未指定的和本身就是一個錯誤),並在初始化列表中元素的數量,所以編譯器之間的失配有一個錯誤。此時鏈接器不參與。

Initializing arrays on the stack this way does not give me a runtime error (I tested it to be sure).

在靜態版本中,編譯器可以計算初始化列表中元素的數量。爲什麼新的動態版不能,要說我沒有好的答案。你會認爲這只是計算初始化列表的一個簡單的步驟,所以有更深的阻止它的東西。那些辯論然後批准標準的人從未考慮過以這種方式分配一個動態數組,或者找不到一種在所有情況下都能正常工作的好方法。相同原因可變長度數組仍然不在標準中。

"And why do I need to specify the array size explicitly even though it will compile and link just fine without? It shouldn't compile, ...." To be clear: If I add the constructor to A and run it, it runs just fine up until the delete[] statement. Only then it crashes but cout << c[0] works as 'expected'

這是因爲你不走運。該構造函數正在寫入您的程序擁有的內存,但未分配給c。打印這些值是有效的,但是在這一點上應該記憶的內容已被覆蓋。這可能會導致您的程序遲早會崩潰。這次晚了。

我懷疑,這是基於特定的猜測,因爲你已經遠遠冒險進入不確定的領域,都在delete[]崩潰是因爲

A* c = new A[] 

分配A[1]並將其分配給c,而不是未能編譯。 c有一個A可以使用。初始化程序列表嘗試填入4,並將3寫入c[0],4,5和6以覆蓋堆控制信息,刪除需要將數據放回。所有看起來都不錯,直到刪除嘗試使用覆蓋的信息。

Oh and this:"Without a constructor all of the members will be initialized to their defaults. int's and most Plain Old Datatypes have no defined default value.". For structs a user defined ctor seems optional because you can initialize a struct by providing arguments corresponding to its data fields.

一個結構具有朝向數據封裝不是類和默認值一個更寬容的態度來public訪問其中一類默認爲private。我從來沒有嘗試過,但我敢打賭,你可以使用相同的結構技巧來初始化一個類的所有公共成員。

好的。剛剛嘗試過。適用於GCC 4.8.1。一般情況下,如果不在標準中查找,就不會提出這種說法。必須得到它的副本。

+0

「爲什麼我需要明確地指定數組的大小,即使它會編譯並且鏈接正好沒有?它不應該編譯......」要清楚: 如果我將構造函數添加到A並運行它,它運行得很好,直到delete []語句。只有它崩潰,但cout << c [0]按照'預期'運作。 – Jupiter

+0

Oh and this:「沒有構造函數,所有的成員都會被初始化爲默認值,int和大多數Plain Old Datatypes都沒有定義默認值。」對於結構體來說,用戶定義的ctor似乎是可選的,因爲您可以通過提供與其數據字段相對應的參數來初始化結構體。 – Jupiter

+0

@BitJunky無法在評論中正確回答這些問題。將它們添加到答案中。 – user4581301

5

這是一個錯誤:

A* c = new A[] { {3}, {4}, {5}, {6} }; 

你必須把尺寸[]內。使用new無法從初始值設定項列表中推導出數組維數。

4放在這裏讓你的代碼對我來說是正確的。

您的編譯器顯然有一個「擴展」,將new A[]視爲new A[1]

如果以標準模式編譯(使用gcc或clang,-std=c++14 -pedantic),這總是一個好主意,編譯器會告訴你這樣的事情。將警告視爲錯誤,除非您確定它們沒有錯誤:)

+0

要迂腐,所有你需要的是'-std = C++ 11'和一個gcc或clang的版本來識別這個選項。你不需要移動到C++ 14,你不需要'-pedantic'。 Clang(正確地)在'new A [] {...}'上,因爲根據標準,在方括號內應該有一個表達式。 gcc不會拒絕'new A []',因爲它將它解釋爲'new A [1]'。然而,它確實妨礙了三個額外的初始化器。 –