簡體   English   中英

是什么原因導致std :: sort()訪問超出范圍的地址

[英]What causes std::sort() to access address out of range

我知道要使用std :: sort(),比較函數必須是嚴格的弱順序,否則它會因訪問超出范圍的地址而崩潰。 https://gcc.gnu.org/ml/gcc-bugs/2013-12/msg00333.html

但是,當比較函數不是嚴格的弱順序時,為什么std :: sort()訪問越界地址? 它試圖比較什么?

我也想知道STL中是否還有其他陷阱需要注意。

第一件事是使用不符合要求的比較器調用算法是未定義的行為,任何事情都會發生......

但除此之外,我假設你有興趣知道如果比較器壞了,哪種類型的實現最終可能會超出范圍。 在首先訪問元素之前,實現是否應該檢查邊界? 即在調用比較器之前

答案是性能,這只是可能導致此類問題的可能因素之一。 排序算法有不同的實現,但通常情況下, std::sort是建立在快速排序變體之上的,它會在不同的排序算法(例如mergesort)上退化,以避免快速排序最差情況下的性能。

quicksort的實現選擇一個pivot,然后圍繞pivot調整輸入,然后獨立地對兩邊進行排序。 選擇樞軸有不同的策略,但常見的是三個中間值:算法獲取第一個,最后一個和中間元素的值,選擇三個中值並將其用作樞軸值。

從概念上講,分區從左側走,直到找到一個不小於樞軸的元素,然后從右側走,試圖找到一個小於樞軸的元素。 如果兩個游標相遇,則分區完成。 如果找到不合適的元素,則交換值,並且該過程在兩個游標確定的范圍內繼續。 從左邊走到找到要交換的元素的循環看起來像:

while (pos < end && value(pos) < pivot) { ++pos; }

雖然通常分區不能假設pivot的值將在范圍內,但quicksort 知道它是,畢竟它選擇了范圍內元素的樞軸。 在這種情況下,常見的優化是將中值的值交換為循環的最后一個元素。 這保證了 pos == end 之前 value(pos) < pivot將為true(最壞情況: pos == end - 1 )。 這里的含義是我們可以放棄檢查范圍的結束,我們可以使用更簡單更快的條件使用unchecked_partition (選擇您的名稱):

while (/*pos < end &&*/ value(pos) < pivot) ++pos;

一切都很好,除了<拼寫comparator(value(pos), pivot) 現在,如果comparator未正確實現,您最終可能會使用comparator(pivot,pivot) == true ,並且光標將超出范圍。

請注意,這只是可以刪除邊界檢查性能的算法優化的一個示例:假設有效順序,如果quicksort 調用此方法之前將數據透視設置為最后一個元素,則無法在上述循環中走出數組修改分區。

回到問題:

在首先訪問元素之前,實現是否應該檢查邊界? 即在調用比較器之前

不,如果它通過證明它不會走出數組來刪除邊界檢查,但是證明是建立在比較器有效的前提下。

std::sort確實要求給定的比較器建立嚴格的弱排序,否則排序實際上沒有多大意義。

至於它訪問超出范圍,你發布的鏈接是一個錯誤報告,即它不應該實際這樣做。 像任何其他軟件一樣的編譯器可以並且將會有錯誤。 正如亞當所指出的,這個特定的錯誤報告被拒絕,因為它不是真正的錯誤。

當你沒有嚴格的弱順序時,究竟發生了什么並沒有被標准定義,這樣做是沒有意義的,因此被標准排除在外。 因此,遺漏未定義 未定義意味着任何事情都可能發生,甚至超出范圍。

至於避免“陷阱”,只需要了解您使用的算法和函數的要求。 對於C ++,我經常使用一個很好的參考站點: cppreference

std::sort的頁面上說:

comp - 比較函數對象(即滿足Compare要求的對象),如果第一個參數小於(即在之前排序)第二個參數,則返回true。

帶有比較說明的鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM