zoukankan      html  css  js  c++  java
  • 使用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()代码。
    在/usr/include/c++/4.1.2/bits 目录下,打开stl_algo.h代码
    搜索sort,如下:
     使用std::sort()排序导致程序core问题分析

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

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

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

     
    在main函数里面定义一个vector<comp>容器对象,先往容器里面添加N个comp元素(N=程序第一个函数值)
     
    全部源代码如下:
    //sorttest.cpp
     
         1  #include <vector>
         2  #include <stdint.h>
         3  #include <string>
         4  #include <iostream>
         5  #include <algorithm>
         6
         7  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 a = 0; a < 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 i = 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          i = 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问题分析

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

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

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

    注:这里为了测试查看方便 ,不再打印日志,把打印日志的也一并注释掉
     
    修改后源码如下:
    //sorttest.cpp
     
         1  #include <vector>
         2  #include <stdint.h>
         3  #include <string>
         4  #include <iostream>
         5  #include <algorithm>
         6
         7  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 a = 0; a < 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问题分析

  • 相关阅读:
    正则表达式把所有Paul替换成Ringo:Paul Puala Pualine paul Paul
    DOM 和 BOM
    新手的grid布局
    css中的单位和css中的颜色表示方法
    css定位
    Winform 通过 WebBrowser 与 JS 交互
    PDF目录编辑器使用介绍
    [.NET] 控制只启动单个指定外部程序
    搭建 Frp 来远程内网 Windows 和 Linux 机子
    CentOs8 nmcli命令行方式操作网卡配置
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318085.html
Copyright © 2011-2022 走看看