關於 std::vector 的下標越界檢查
C語言(加了層語法糖的彙編)為了效能並不支援陣列的越界檢查,每次檢查會多付出2-3倍的時間。
而vector以at的形式支援越界檢查,但也支援C語言風格的[]高效訪問,沒錯C++提供了足夠的自由。
當要獲取 std::vector 的第 n 個元素,下面幾種方式都可以:
-
std::vector<int> vec;
-
size_t n = 1;
-
int & i = vec[n];
-
int & j = *(vec.begin() + n);
-
int & k = vec.at(n);
但是如果 n 超過了 vector 的下標範圍,在幾種方式的結果就有區別了。
在 linux 平臺只有 at(n) 會丟擲 std::out_of_range 異常,而其他兩個都不會。
檢視標準模板庫程式碼,可以看到:
-
// element access
-
/**
-
* @brief Subscript access to the data contained in the %vector.
-
* @param n The index of the element for which data should be
-
* accessed.
-
* @return Read/write reference to data.
-
*
-
* This operator allows for easy, array-style, data access.
-
* Note that data access with this operator is unchecked and
-
* out_of_range lookups are not defined. (For checked lookups
-
* see at().)
-
*/
-
reference
-
operator[](size_type __n)
-
{ return *(this->_M_impl._M_start + __n); }
有說明:通過 operator[] 獲取陣列元素,不會檢查下標有效性,需要檢查的時候使用 at 介面
在 windows 平臺 VS 環境下,都會丟擲異常,VC 下的標準庫是這樣現實的:
-
reference operator[](size_type _Pos)
-
{ // subscript mutable sequence
-
#if _HAS_ITERATOR_DEBUGGING
-
if (size() <= _Pos)
-
{
-
_DEBUG_ERROR("vector subscript out of range");
-
_SCL_SECURE_OUT_OF_RANGE;
-
}
-
#endif /* _HAS_ITERATOR_DEBUGGING */
-
_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
-
return (*(_Myfirst + _Pos));
-
}
在 Debug 配置下, _HAS_ITERATOR_DEBUGGING 預設定義為 1,但是即使強制定義為 0,也有異常,因為還有一個 _SCL_SECURE_VALIDATE_RANGE 檢查。所以即使在 Release 配置下,下標檢查也是存在的。
如果把上面的程式碼中的“引用”符號去掉,像這樣:
-
std::vector<int> vec;
-
size_t n = 1;
-
int i = vec[n];
-
int j = *(vec.begin() + n);
-
int k = vec.at(n);
那麼 at(n) 仍然會丟擲 std::out_of_range 異常,其他兩個會出現什麼情況呢,也許是記憶體訪問異常,也許是其他詭異的錯誤。
在 vec 非空的情況下,即使下標越界,也有可能對應的記憶體是可讀寫的,至於讀到的是什麼內容,或者寫到什麼地方,就是隨機事件了。