1. 程式人生 > >關於 std::vector 的下標越界檢查

關於 std::vector 的下標越界檢查

C語言(加了層語法糖的彙編)為了效能並不支援陣列的越界檢查,每次檢查會多付出2-3倍的時間。

而vector以at的形式支援越界檢查,但也支援C語言風格的[]高效訪問,沒錯C++提供了足夠的自由。

當要獲取 std::vector 的第 n 個元素,下面幾種方式都可以:

  1. std::vector<int> vec;

  2. size_t n = 1;

  3. int & i = vec[n];

  4. int & j = *(vec.begin() + n);

  5. int & k = vec.at(n);

但是如果 n 超過了 vector 的下標範圍,在幾種方式的結果就有區別了。

在 linux 平臺只有 at(n) 會丟擲 std::out_of_range 異常,而其他兩個都不會。

 檢視標準模板庫程式碼,可以看到:

  1. // element access

  2. /**

  3. * @brief Subscript access to the data contained in the %vector.

  4. * @param n The index of the element for which data should be

  5. * accessed.

  6. * @return Read/write reference to data.

  7. *

  8. * This operator allows for easy, array-style, data access.

  9. * Note that data access with this operator is unchecked and

  10. * out_of_range lookups are not defined. (For checked lookups

  11. * see at().)

  12. */

  13. reference

  14. operator[](size_type __n)

  15. { return *(this->_M_impl._M_start + __n); }

有說明:通過 operator[] 獲取陣列元素,不會檢查下標有效性,需要檢查的時候使用 at 介面

在 windows 平臺 VS 環境下,都會丟擲異常,VC 下的標準庫是這樣現實的:

  1. reference operator[](size_type _Pos)

  2. { // subscript mutable sequence

  3. #if _HAS_ITERATOR_DEBUGGING

  4. if (size() <= _Pos)

  5. {

  6. _DEBUG_ERROR("vector subscript out of range");

  7. _SCL_SECURE_OUT_OF_RANGE;

  8. }

  9. #endif /* _HAS_ITERATOR_DEBUGGING */

  10. _SCL_SECURE_VALIDATE_RANGE(_Pos < size());

  11. return (*(_Myfirst + _Pos));

  12. }

在 Debug 配置下, _HAS_ITERATOR_DEBUGGING 預設定義為 1,但是即使強制定義為 0,也有異常,因為還有一個 _SCL_SECURE_VALIDATE_RANGE 檢查。所以即使在 Release 配置下,下標檢查也是存在的。

如果把上面的程式碼中的“引用”符號去掉,像這樣:

  1. std::vector<int> vec;

  2. size_t n = 1;

  3. int i = vec[n];

  4. int j = *(vec.begin() + n);

  5. int k = vec.at(n);

那麼 at(n) 仍然會丟擲 std::out_of_range 異常,其他兩個會出現什麼情況呢,也許是記憶體訪問異常,也許是其他詭異的錯誤。

在 vec 非空的情況下,即使下標越界,也有可能對應的記憶體是可讀寫的,至於讀到的是什麼內容,或者寫到什麼地方,就是隨機事件了。