使用std::sort()排序导致程序core问题分析

一、问题
std::sort()在排序的时候,如果对排序中的仿函数对相等的值返回true,会导致程序core掉。

二、解决办法
让比较函数对相等的值返回false

三、原因分析
std::sort()在排序是分两步进行的,首选进行快速排序,再对快速排序后的进行插入排序。但如果对于容器里面的对象个数较少的时候,快速排序的性能并不理想,所以STL的std::sort()里面增加了一个枚举常量_S_threshold ,用来判断容器里面对象个数,看是否需要进行快速排序。
我们知道快速排序主要就点:
(1)、设置一个比较值,遍历一次所有数据,把小于比较值的放左边,大于比较值的放右边
(2)、对左右两边的数据进行递归快速排序
STL的std::sort()的快速排序里面,对于第一点中的所有数据进行与中间值比较的时候是 无边界保护的,它认为用来排序的容器里面对象恒有一个大值和小值,也就是在容器的对象里面,通过comp()函数进行比较,恒有两个值比较后返回false。问题也就出在这里,当我们的容器里面所有值都相等,而comp()函数对相等返回true的时候,在进行快速排序的时候,迭代器就会越界失效。
 
 
源代码如下:
//stl_algo.h
使用std::sort()排序导致程序core问题分析

使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析
 
四、源码分析
让我们来跟一下源码,首选查到std::sort()代码。
在/usr/include/c++/4.1.2/bits 目录下,打开stl_algo.h代码
搜索sort,如下:
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

注:sort有两个版本,第一个是直接调用容器对象小于操作比较的,第二个是带仿函数的版本。我们取第二个版本。
 
真正的比较在2749行开始,进行快速排序。
 
我们再搜索一下__introsort_loop快速排序函数,如下图:
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

这里面我们看如下几点:
(1)、第2662行里面把容器里面的元素个数与_S_threshold 进行比较,我们搜索_S_threshold 发现,原来是个枚举常量
使用std::sort()排序导致程序core问题分析

使用std::sort()排序导致程序core问题分析
这个就是判断是否需要对容器里面的元素进行快速排序。
 
(2)、第2664行,这里对__depth_limit与0进行比较,这个是快速排序的递归的深度。这个判断是STL对快速排序的一个优化处理,这里不再细说这个问题,详见:《effective stl 》条款31.
(3)、第2671行,这里才真正进行快速排序的代码,其中的std::__median()函数用来取容器中第一个值、中间值 和 最后一个值的中间。
 
下面让我们进入__unguarded_partition ()这个函数
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

进入这个函数后,这里一看就很明显了,第2199行代码,如下
while(__comp(*__first,__pivot))
++__first
这两行代码里面,__pivot是中间值,__first是迭代器,假设我们的__comp函数对于相等的值返回true,那么如果一个容器里面最后一段元素所有值都相等,那么__comp(*__first,__pivot)就恒为真。
迭代器往前移的时候,终会移过最后一个元素,于是迭代器失效,程序core。
 
五、实例
上面啰嗦了这么多,让我们来用个实例跑一下。
 
(1)、源代码
我们定义个对象,里面有两个成员变量i,j,我们重载小于操作符,通过成员变量i进行排序,对于相等的i时候,返回true。如下:
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

 
在main函数里面定义一个vector<comp>容器对象,先往容器里面添加N个comp元素(N=程序第一个函数值)
 
全部源代码如下:
//sorttest.cpp
 
         #include <vector>
            #include  <stdint.h>
            #include  <string>
            #include  <iostream>
            #include  <algorithm>
          6
            using  namespace  std;
          8
          9
        10    class  comp
        11    {
        12    public:
        13                    int  i;
        14                    int  j;
        15    public:
        16                    comp(){}
        17                    ~comp(){}
        18    public:
        19                    bool  operator<(const  comp  r)  const
        20                    {
        21                                    return  this->i  <=  r.i;
        22                    }
        23
        24    };
        25
        26
        27
        28
        29    int  main(int  argc,char  **  argv)
        30    {
        31                    if(argc  !=  2)
        32                    {
        33                                    cout<<"wrong  arguments.sorttest  <vSize>"<<endl;
        34                                    return  0;
        35                    }
        36
        37                    std::vector<comp>  v;
        38
        39
        40                    //insert  object  to  vector
        41                    comp  obj;
        42                    obj.i  10;
        43                    for(int  0;  strtoul(argv[1],NULL,10);++a)
        44                    {
        45                                    obj.j  a;
        46                                    v.push_back(obj);
        47                    }
        48
        49
        50
        51                    //print  sort  before  value
        52                    int  1;
        53                    for(vector<comp>::iterator  it  v.begin();it  !=  v.end();  it++)
        54                    {
        55                                    std::cout<<"befort  sort:"<<i<<":"<<"i="<<it->i<<";j="<<it->j<<endl;
        56                                    i++;
        57                    }
        58
        59
        60                    //sort
        61                    std::sort(v.begin(),v.end());
        62
        63
        64                    //print  end  sort  value
        65                    1;
        66                    for(vector<comp>::iterator  it  v.begin();it  !=  v.end();  it++)
        67                    {
        68                                    std::cout<<"befort  sort:"<<i<<":"<<"i="<<it->i<<";j="<<it->j<<endl;
        69                                    i++;
        70                    }
        71
        72                    return  0;
        73    }
 
(2)、编译
  g++  -g  -o  sorttest  sorttest.cpp
 
(3)、测试
测试一:
我们先往vector<>容器里面插入10个元素,看下结果:
运行 ./sorttest  10
 
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

能正确运行。
 
测试二:
我们往vector<>容器里面添加16个元素,看下结果:
运行 ./sorttest 16
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

正确运行
 
测试三:
我们往vector<>里面插入17个元素,如下:
./sorttest 17
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

程序core掉
 
这里,当容器里面的元素个数超过16个,到17个后程序就core掉,这也说明了上面讲到的,对于std::sort()里面,是否进行快速排序的前提条件是,容器里面元素的个数要大于_S_threshold 的枚举常量值,而stl的这个默认值是16,所以当容器里面元素个数超过16个的时候,就会进行快速排序,而这个测试程序的容器里面所以有的值对小于操作符都是返回true的,所以程序core掉。
 
我们再修改一下代码,把重载小于操作符的 <= 比较改成 < ,再来看看程序是否会core。
如下:
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

注:这里为了测试查看方便 ,不再打印日志,把打印日志的也一并注释掉
 
修改后源码如下:
//sorttest.cpp
 
          #include <vector>
            #include  <stdint.h>
            #include  <string>
            #include  <iostream>
            #include  <algorithm>
          6
            using  namespace  std;
          8
          9
        10    class  comp
        11    {
        12    public:
        13                    int  i;
        14                    int  j;
        15    public:
        16                    comp(){}
        17                    ~comp(){}
        18    public:
        19                    bool  operator<(const  comp  r)  const
        20                    {
        21                                    //return  this->i  <=  r.i;
        22                                    return  this->i  r.i;
        23                    }
        24
        25    };
        26
        27
        28
        29
        30    int  main(int  argc,char  **  argv)
        31    {
        32                    if(argc  !=  2)
        33                    {
        34                                    cout<<"wrong  arguments.sorttest  <vSize>"<<endl;
        35                                    return  0;
        36                    }
        37
        38                    std::vector<comp>  v;
        39
        40
        41                    //insert  object  to  vector
        42                    comp  obj;
        43                    obj.i  10;
        44                    for(int  0;  strtoul(argv[1],NULL,10);++a)
        45                    {
        46                                    obj.j  a;
        47                                    v.push_back(obj);
        48                    }
        49
        50
        51   
        60
        61
        62                    //sort
        63                    std::sort(v.begin(),v.end());
        64
        65   
        74                    std::cout<<"sort  success"<<endl;
        75                    return  0;
        76    }
 
 
编译运行,我们直接往容器里面插入100个元素,运行如下:
使用std::sort()排序导致程序core问题分析
  使用std::sort()排序导致程序core问题分析

正确运行。
 
至此,用《effective  stl 》里面一句话总结全文: 永远让比较函数对相等的值返回false
 
---(全文完)---
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值