C++用std:sort自定义比较器core dump的原因

下面这段代码稳定复现bug

#include <bits/stdc++.h>

using namespace std;
bool compare (int a, int b){
    return a <= b;
}
int main()
{
    vector<int> vec;
    for(int i = 0; i < 17; i++)
        vec.push_back(1);  // 数组中是17个1
    sort(vec.begin(), vec.end(), compare);
    for (auto i : vec)
        cout << i << endl;
}

修复bug有两种方式

  1. 将比较器中的a<=b改成a<b
  2. 将数组长度17改成16

熟悉stl规范的知道,出现bug的原因是sort要求传入一个\color{red}{弱比较器},即满足:当a和b相等的时候,a compare b返回false,显然<=不符合条件,<是符合的。

那core是怎么产生的呢,具体原因如下:

  1. 为什么和长度有关?
    stl的sort函数在容器长度小于16时会直接使用插入排序,大于16时会先使用快排将数据分成长度小于16的数据段,再在段内使用插入排序。因此长度小于16时不会出现bug。
  2. core在哪里?
    stl中使用的快排经过优化,选择头、尾、中间,三个数的中位数作为pivot,代码如下:
inline _RandomAccessIterator
    __unguarded_partition_pivot(_RandomAccessIterator __first,
                _RandomAccessIterator __last, _Compare __comp)
    {
      _RandomAccessIterator __mid = __first + (__last - __first) / 2;  // 取中间
      std::__move_median_to_first(__first, __first + 1, __mid, __last - 1, __comp);  // 将头、尾、中间三者的中位数作为pivot并放在容器头
      return std::__unguarded_partition(__first + 1, __last, __first, __comp);  // 将小于pivot的数放在左边,大于pivot的数放在右边,pivot在中间
    }

问题出现在__unguarded_partition函数,前一步是取了3个值的中位数作为pivot的,因此这个函数有一个假设,即全部数据中一定有至少一个值比pivot大,一个比pivot小。

  _RandomAccessIterator
  __unguarded_partition(_RandomAccessIterator __first,
            _RandomAccessIterator __last,
            _RandomAccessIterator __pivot, _Compare __comp)
  {
    while (true)
  {
    while (__comp(__first, __pivot))    //找到比pivot大的那个数的位置,当数组中的所有数相等的时候,这句会一直执行直到越界在界外找到符合条件的位置
      ++__first;
    --__last;
    while (__comp(__pivot, __last))  //找到比pivot小的那个数的位置,当数组中的所有数相等的时候,这句会一直执行直到越界在界外找到符合条件的位置
      --__last;
    if (!(__first < __last))
      return __first;
    std::iter_swap(__first, __last);
    ++__first;
  }
  }

当传入的比较器不是弱比较器时,如果数组中所有数字相等,注释的2个while循环无法正确退出。相反,如果比较器满足1 compare 1为false,则不会越界。

除此之外

弱比较器还用在set、map等有序容器中,map的key要求唯一,判断两个key是否相等的方式是!(a<b)&&!(b<a),即a不小于b同时b不小于a则认为a和b相等。如果传入的是<=,则a<=b成立,b<=a也成立,会认为这两个数不相等而插入,因此打破了map的树结构,引入了未定义行为。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。