[英]What happens if you dereference `new int`?
以下是安全的嗎?
*(new int);
我輸出為0
。
它是未定義的,因為您正在讀取具有不確定值的對象。 表達式new int()
使用零初始化,保證零值,而new int
(沒有括號)使用默認初始化,為您提供不確定的值。 這實際上與說:
int x; // not initialised
cout << x << '\n'; // undefined value
但另外,由於您立即取消引用指向您剛剛分配的對象的指針,並且不將指針存儲在任何位置,這就構成了內存泄漏。
請注意,這種表達式的存在並不一定會使程序形成錯誤; 這是一個非常有效的程序,因為它在讀取之前設置了對象的值:
int& x = *(new int); // x is an alias for a nameless new int of undefined value
x = 42;
cout << x << '\n';
delete &x;
這是未定義的行為 ( UB ),因為您正在訪問一個不確定的值,C ++ 14顯然會產生這種未定義的行為。 我們可以看到沒有初始化程序的new
默認初始化 ,來自草案C ++ 14標准第5.3.4
節新段落17 ( 強調我的未來 ):
如果省略new-initializer,則默認初始化對象(8.5)。 [注意:如果未執行初始化,則對象具有不確定的值。 - 尾注]
對於int,這意味着一個不確定的值,來自第8.5
節第7段,其中說:
默認初始化T類型的對象意味着:
- 如果T是(可能是cv限定的)類類型(第9節),則調用T的默認構造函數(12.1)(如果T沒有默認構造函數或重載解析(13.3),則初始化是錯誤的歧義或在初始化上下文中刪除或無法訪問的函數中;
- 如果T是數組類型,則每個元素都是默認初始化的;
- 否則,不執行初始化。
我們可以從8.5
節看到生成不確定值是未定義的:
如果沒有為對象指定初始化程序,則默認初始化該對象。 當獲得具有自動或動態存儲持續時間的對象的存儲時, 該對象具有不確定的值 ,並且如果沒有對該對象執行初始化,則該對象保留不確定的值,直到該值被替換(5.17)。 [注意:具有靜態或線程存儲持續時間的對象是零初始化的,請參見3.6.2。 - 結束注釋如果評估產生不確定的值,則行為未定義,但以下情況除外
並且所有異常都與unsigned narrow char有關 ,而int不是。
喬恩提出了一個有趣的例子:
int& x = *(new int);
為什么這不是未定義的行為可能不會立即顯而易見。 要注意的關鍵點是產生值的未定義行為,但在這種情況下不會產生任何值。 我們可以通過參見8.5.3
參考文獻來看到這一點, 參考資料包括參考文獻的初始化,它說:
對類型“cv1 T1”的引用由類型“cv2 T2”的表達式初始化,如下所示:
- 如果引用是左值引用和初始化表達式
- 是左值(但不是位字段),“cv1 T1”與“cv2 T2”引用兼容,或者
接着說:
然后在第一種情況下將引用綁定到初始化表達式lvalue [...] [注意: 通常的左值到右值(4.1),數組到指針(4.2)和函數到指針(4.3) )當完成對左值的這種直接綁定時, 不需要標准轉換,因此被抑制 。 - 尾注]
計算機可能具有“陷阱” int
值:無效值,例如校驗和位,當它與預期狀態不匹配時會引發硬件異常。
通常,未初始化的值會導致未定義的行為。 首先初始化它。
否則,不,取消引用new-expression沒有任何錯誤或非常不尋常。 以下是使用您的構造的一些奇怪但完全有效的代碼:
int & ir = * ( new int ) = 0;
…
delete & ir;
首先, Shafik Yaghmour在答案中提到了標准。 這是最好,最完整和最權威的答案。 盡管如此,讓我試着給你一些具體的例子來說明前面提到的觀點。
此代碼安全,格式良好且有意義:
int *p = new int; // ie this is a local variable (ptr) that points
// to a heap-allocated block
但是,您不能取消引用指針,因為這會導致未定義的行為 。 IE可能會得到0x00或0xFFFFFFFF,或指令指針(也就是英特爾上的RIP寄存器)可能會跳轉到隨機位置。 電腦可能會崩潰。
int *p = new int;
std::cout << *p; // Very, bad. Undefined behavior.
諸如Valgrind和ASan之類的運行時檢查程序將捕獲該問題,標記它並使用一個很好的錯誤消息崩潰。
但是,初始化已分配的內存塊是完全正常的:
int *p = new int;
*p = 0;
背景信息:編寫規范的這種特殊方式對於性能非常有用,因為實現替代方案非常昂貴。
請注意,根據標准參考,有時初始化很便宜,因此您可以執行以下操作:
// at the file scope
int global1; // zero-initialized
int global2 = 1; // explicitly initialized
void f()
{
std::cout << global1;
}
這些東西進入可執行文件的部分(.bss和.data),並由OS加載程序初始化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.