C++使用sort排序导致的coredump

本文探讨了C++ STL中的StrictWeakOrdering概念,特别是std::sort导致的core问题,通过实例分析了一个错误的比较函数并提出解决方案。重点在于理解如何确保排序函数遵循严格的弱顺序性质以避免程序崩溃。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Strict Weak Ordering(严格弱顺序)

Strict Weak Ordering是C++ STL中各种算法都用到的一个概念。

  1. Strict:s < s 要必须返回false;
  2. Weak:s < t 和 t < s都返回false时,就说明s == t;
  3. Ordering:s < t为true 并且 t < z为true,则s < z也必须为true。

二、std::sort导致core

对于std::sort(),当容器里面元素的个数大于_S_threshold(stl的这个默认值是16)的枚举常量值时,会使用快速排序。

  /// This is a helper function for the sort routine.
  template<typename _RandomAccessIterator, typename _Size, typename _Compare>
    void
    __introsort_loop(_RandomAccessIterator __first,
		     _RandomAccessIterator __last,
		     _Size __depth_limit, _Compare __comp)
    {
      while (__last - __first > int(_S_threshold))
	{
	  if (__depth_limit == 0)
	    {
	      _GLIBCXX_STD_A::partial_sort(__first, __last, __last, __comp);
	      return;
	    }
	  --__depth_limit;
	  _RandomAccessIterator __cut =
	    std::__unguarded_partition_pivot(__first, __last, __comp);
	  std::__introsort_loop(__cut, __last, __depth_limit, __comp);
	  __last = __cut;
	}
    }
  /// This is a helper function...
  template<typename _RandomAccessIterator, typename _Tp, typename _Compare>
    _RandomAccessIterator
    __unguarded_partition(_RandomAccessIterator __first,
			  _RandomAccessIterator __last,
			  const _Tp& __pivot, _Compare __comp)
    {
      while (true)
	{
	  while (__comp(*__first, __pivot))
	    ++__first;
	  --__last;
	  while (__comp(__pivot, *__last))
	    --__last;
	  if (!(__first < __last))
	    return __first;
	  std::iter_swap(__first, __last);
	  ++__first;
	}
    }

注意这里:

while (__comp(*__first, __pivot))
	    ++__first;

这两行代码里面,__pivot是中间值,__first是迭代器,假设我们的__comp函数对于相等的值返回true,那么如果一个容器里面最后一段元素所有值都相等,那么__comp(*__first,__pivot)就恒为真。迭代器往前移的时候,终会移过最后一个元素,于是迭代器失效,程序core。

我们正是在生产环境遇到了这个问题导致了core。

其实下面的两行代码也可能导致core,原理是一样的,只是当时没有遇到,后面在代码测试过程中介绍。

while (__comp(__pivot, *__last))
	    --__last;

如果一个comp函数要满足Strict Weak Ordering,意味着它应该满足如下特征:

  1. 反自反性:也即comp(x, x)必须是false
  2. 非对称性:也即如果comp(x, y)和comp(y, x)的结果必然相反
  3. 可传递性:也即如果comp(x, y)为true,comp(y, z)为true,那么comp(x, z)必然为true

下面这个cmp函数明显违背了1、2两条。

bool cmp(int a,int b)
{
 return a<=b;
}

在Effective STL一书中有这么一条:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPkl26jQ-1648120967889)(/upload/2021/10/image-f72af105562c4dd49977327125f67df5.png)]

三、一个测试例子

#include <vector>
#include <algorithm>
using namespace std;

struct A
{
	A(int _a) :  n (_a){}
	int n;
};

bool comp(A* a, A* b)
{
	return a->n <= b->n;
}


int main(int argc, char **argv)
{
	vector<A*> S;
	for (int i = 0; i < 100; ++i)
	{
		S.push_back(new A(i));
		S.push_back(new A(i));
	}

	std::sort(S.begin(), S.end(), comp);
	
	return 0;
}

上面这个因为while (__comp(__pivot, *__last))一直是true,__last迭代器越界失效,导致程序core。

#include <vector>
#include <algorithm>
using namespace std;

struct A
{
	A(int _a) :  n (_a){}
	int n;
};

bool comp(A* a, A* b)
{
	return a->n <= b->n;
}


int main(int argc, char **argv)
{
	vector<A*> S;
	for (int i = 0; i < 100; ++i)
	{
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
		S.push_back(new A(i));
	}

	std::sort(S.begin(), S.end(), comp);
	
	return 0;
}

上面这个因为while (__comp(__pivot, *__first))一直是true,__first迭代器越界失效,导致程序core。

四、结论:总是让比较函数在等值情况下返回false

<think>好的,我现在需要帮助用户找到学习C++的资源和学习方法,从入门到精通。首先,我要回顾用户提供的引用内容,看看里面有哪些有用的信息。引用[1]提到C++的基础知识、应用领域和学习路线,引用[2]推荐了一些书籍和安装开发环境,引用[3]则详细列出了教程目录和项目案例。 用户明确想要教程资源和方法,所以应该分步骤来组织回答。首先推荐书籍和在线课程,这部分引用[2]和[3]都有提到,比如《C++ Primer》和在线教程。然后安装开发环境,引用[2]提到了这点,需要具体说明如何安装,比如Visual Studio或Code::Blocks。 接下来是学习路径,引用[3]的目录很详细,可以分阶段:基础语法、面向对象、高级特性、模板和STL、实战项目。每个阶段要列出关键知识点,比如基础语法包括变量、函数、控制结构,面向对象包括类和继承,高级特性如多态和虚函数,模板和STL部分需要强调标准库的应用。 实践方法方面,用户需要知道如何练习,比如做练习题和项目,引用[3]中的项目案例可以作为例子。还要推荐社区和论坛,帮助解决问题。常见问题部分要解答学习难点,比如指针和内存管理,以及如何应用C++到不同领域,如游戏开发或嵌入式系统。 最后要生成相关问题,可能涉及性能优化、就业方向、调试技巧等。需要确保所有推荐的资源和方法都有引用支持,并且结构清晰,步骤明确。同时检查是否符合用户的要求,比如中文回答、LaTeX格式正确,但问题中没有数学公式,所以可能不需要处理数学内容。总之,整理出系统化的学习路线,结合理论和实践,引用提供的资料,帮助用户高效学习C++。</think>### C++ 学习教程:从入门到精通 以下是系统化的学习路径与资源推荐,结合理论与实践,帮助你高效掌握 C++ 编程。 --- #### **一、学习资源推荐** 1. **经典书籍** - 《C++ Primer》:全面覆盖基础语法与核心概念,适合逐章精读[^2]。 - 《Effective C++》:深入理解 C++ 编程规范与最佳实践。 - 《C++ Programming Language》(Bjarne Stroustrup 著):语言设计者亲撰,适合进阶学习。 2. **在线教程** - **C++ 官方文档**(cppreference.com):权威语法参考与标准库说明。 - **菜鸟教程、RUNOOB**:中文入门教程,适合快速上手基础语法[^3]。 - **YouTube/B站**:搜索“C++ 从入门到精通”系列视频,直观学习编程操作。 3. **开发环境** - **Visual Studio**(Windows):功能强大的 IDE,集成调试工具[^2]。 - **Code::Blocks**(跨平台):轻量级 IDE,适合初学者。 - **CLion**(JetBrains 出品):专业级工具,支持现代 C++ 特性。 --- #### **二、系统化学习路径** ##### **阶段 1:基础语法(1-2 个月)** - **核心内容**: - 变量、数据类型、运算符(如 `int`, `float`, `指针`)。 - 控制结构(`if-else`, `for`, `while`)。 - 函数、作用域与头文件(如 `#include <iostream>`)。 - 数组、字符串与基本输入输出(`cin`, `cout`)。 - **实践建议**: - 编写简单程序(如计算器、斐波那契数列)。 - 理解内存模型与指针操作(例如 `int* p = &a;`)。 ##### **阶段 2:面向对象编程(2-3 个月)** - **核心内容**: - 类与对象(`class`, `public/private`)。 - 构造函数与析构函数(初始化列表、`~ClassName()`)。 - 继承与多态(`virtual`, 纯虚函数)。 - 操作符重载(如 `+`, `<<`)。 - **实践建议**: - 实现小型管理系统(如学生信息管理)。 - 通过项目理解 `this指针` 和 `友元函数`[^3]。 ##### **阶段 3:高级特性(2 个月)** - **核心内容**: - 模板编程(函数模板 `template <typename T>`、类模板)。 - 标准模板库(STL):容器(`vector`, `map`)、算法(`sort`, `find`)。 - 异常处理(`try-catch`)、命名空间(`namespace`)。 - **实践建议**: - 使用 `STL` 实现高效算法(如快速排序、哈希表)。 - 学习文件操作(`fstream`)与多线程基础。 ##### **阶段 4:实战与优化(持续)** - **项目案例**: - 小型游戏开发(如控制台贪吃蛇)。 - 网络通信程序(基于 `Socket` 实现客户端/服务器)。 - 性能优化实践(内存管理、`constexpr` 与移动语义)。 --- #### **三、学习方法与技巧** 1. **编码实践** - 每日坚持编写代码,通过平台如 **LeetCode** 或 **Codewars** 刷题。 - 参与开源项目(GitHub),学习代码规范与协作开发。 2. **调试能力** - 掌握调试工具(如 `GDB` 或 IDE 内置调试器)。 - 学会分析核心转储(Core Dump)与内存泄漏检测(Valgrind)。 3. **社区交流** - 加入论坛(Stack Overflow、知乎专栏)解决疑难问题。 - 关注 C++ 标准更新(如 C++11/17/20 新特性)。 --- #### **四、常见问题解答** 1. **Q:C++ 学习难点是什么?** - **A**:指针与内存管理、多态与虚函数机制、模板元编程[^3]。 2. **Q:如何高效应用 C++?** - **A**:结合领域需求(如游戏开发用 `Unreal Engine`,嵌入式开发用 `ARM` 架构优化)[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值