2016-11-14 69 views
9

我用std::array替換了一箇舊的樣式數組,但後來我意識到編譯器(至少MSVC)正在執行更少的邊界檢查。考慮下面的代碼:爲什麼MSVC編譯器沒有檢測到std :: array出邊界訪問

double myArray[10]; 
myArray[11] = 3.0; 

std::array<double,10> myStdArray; 
myStdArray[11] = 3.0; 

佛myArray的,警告報告:

警告C4789:大小80個字節將被溢出的緩衝 'myArray的';將從偏移量開始寫入8個字節88

對於myStdArray,根本沒有報警。

這是編譯器中的「bug」還是std::array的實現方式不允許報告這種警告?如果是這樣,是否值得使用std::array,這裏似乎不太安全......

+1

如果您的索引在編譯時已知,則可以使用'std :: get'來獲得編譯時錯誤。 – krzaq

+2

C++從來沒有對任何類型的數組進行邊界檢查,它永遠不會。如果你想邊界檢查你應該使用['at'](http://en.cppreference.com/w/cpp/container/array/at)。儘管我在大多數情況下建議使用普通C數組的'std :: array',但不是爲了「安全」,而是爲了易用性。 –

+2

編譯器無法看到'std :: array'的內部實現,甚至不能看到足以提供與本地數組相同的消息的合約。 – EJP

回答

7

如果您的訪問超出範圍,您可以使用std::get以獲得有保證的錯誤。當然,編譯時必須知道索引。

std::array<double,10> myStdArray; 
std::get<9>(myStdArray) = 3.0; // ok 
std::get<11>(myStdArray) = 3.0; // error 

至於你的問題的另一部分:這可能是我的一部分猜想,但標準庫應該在路上編譯器知道它的合同並沒有什麼決策被認爲是「神奇」它不可能執行事實檢查。

以下示例很容易證明。無論鐺和gcc的Elid贊成直接寫的memset撥打以下功能:

void zero_int(int* ptr) 
{ 
    memset(ptr, 0, sizeof(int)); 
} 

compiler explorer

所以,據我所知沒有什麼阻止編譯器從發射代碼中的警告,潛在的執行難度/成本除外。

7

myArray[11]是編譯器知道的以及編譯器可以警告的語言內置功能。

myStdArray[11]myStdArray.operator[](11)的簡寫,這是一個函數調用,其中11在參數類型的範圍內很好。對此的警告要求查看operator[]的函數體,這通常只在函數被內聯時纔會發生。

此外,供應商擴展可以使myStdArray.operator[](11)明確定義,例如,因爲在調試模式下中止程序,並且在這樣的實現中它很難得到有用的編譯時警告。

但是,有了這樣的擴展,增加的運行時安全性仍可能超過缺少編譯時警告。

相關問題