C++ debug和release版本运行结果不一致浅析

问题

最近在写 AVL 树的问题,其中涉及到大量的指针操作。但由于出现了 bug ,在没有修复时出现了单步调试和直接编译运行的结果不一样的情况。大致情况是:单步调试能够根据逻辑把结果运行出来,但是编译运行会出现程序异常中断的情况

问题代码

#include<iostream>
#include <cmath>
using namespace std;

template<class K,class V>
struct AVLTreeNode{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	K _key;
	V _value;
	int  _bf;   //平衡因子

	AVLTreeNode(const K& key,const V& value)
		:_key(key)
		, _value(value)
		, _left(NULL)
		, _right(NULL)
		, _parent(NULL)
		, _bf(0)
	{}
};

template<class K,class V>
class AVLTree{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		:_root(NULL)
	{}
	bool Insert(const K& key, const V& value)
	{
		if (_root == NULL)
		{
			_root = new Node(key,value);
			return true;
		}
		Node* cur = _root;
		Node* parent = NULL;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key,value);
		if (parent->_key > key)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		//更新平衡因子
		//不平衡,则进行旋转
		while (parent)
		{
			if (parent->_right==cur)
				parent->_bf++;
			else
				parent->_bf--;
			//父节点平衡因子为0时,退出(说明父节点的两边高度一样,算路径长度的话都一样,没有影响)
			if (parent->_bf == 0)
				break;
			//父节点平衡因子为1或-1的时候(说明是从0+1或0-1得来的),父节点两边高度不同,故需要继续更新平衡因子
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = cur->_parent;
			}
			//父节点平衡因子为2或-2时,旋转
			else  //(parent->_bf==2||parent->_bf==-2) 旋转
			{
				if (parent->_bf == -2)
				{
					if (cur->_bf == -1)//右单旋
					{
						_RotateR(parent);
					}
					else //(cur->_bf==1) 左右单旋
					{
						_RotateLR(parent);
					}
				}
				else //(parent->_bf==2)
				{
					if (cur->_bf == 1)  //左单旋
					{
						_RotateL(parent);
					}
					else  //(cur->_bf==-1)右左单旋
					{
						_RotateRL(parent);
					}
				}
				break;
			}
		}
	}
	Node* Find(const K& key)
	{
		if (_root == NULL)
			return NULL;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
				cur = cur->_left;
			else if (cur->_key < key)
				cur = cur->_right;
			else
				return cur;
		}
		return NULL;
	}
	bool Remove(const K& key)
	{
		if (_root == NULL)
			return false;

		Node* parent = NULL;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				Node* del;
				if (cur->_right == NULL)
				{
					del = cur;
					if (parent == NULL)
					{
						_root = cur->_left;
						//_root->_bf = 0;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
							parent->_bf++;
						}
						else
						{
							parent->_right = cur->_left;
							parent->_bf--;
						}
					}
					delete del;
				}
				else if (cur->_left == NULL)
				{
					del = cur;
					if (parent == NULL)
					{
						_root = cur->_right;
						_root->_bf = 0;
						cout << "cur : " << cur->_key << endl;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
							parent->_bf++;
						}
						else
						{
							parent->_right = cur->_right;
							parent->_bf--;
						}
					}
					delete del;
				}
				else
				{
					parent = cur;
					Node* left = cur->_right;
					while (left->_left)
					{
						parent = left;
						left = left->_left;
					}
					del = left;
					cur->_key = left->_key;
					cur->_value = left->_value;
					if (parent->_left == left)
					{
						parent->_left = left->_right;
						parent->_bf++;
					}
					else
					{
						parent->_right = left->_right;
						parent->_bf--;
					}

					delete del;
				}
				break;
			}
		}
		if (cur == NULL)
		{
			return false;
		}
		while (parent)
		{
			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				break;
			}
			else //parent->_bf=2||parent->_bf=-2
			{
				if (parent->_bf == -2)
				{
					if (cur->_bf == -1)
						_RotateR(parent);
					else  //cur->_bf=1
						_RotateLR(parent);
				}
				else
				{
					if (cur->_bf == 1)
						_RotateL(parent);
					else
						_RotateRL(parent);
				}
				break;
			}
		}
		return true;
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	//判断这棵树是否是平衡搜索树
	bool IsBlance()
	{
		return _IsBlance(_root);
	}
protected:
	void _RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR=subL->_right;

		parent->_left = subLR;
		if (subLR != NULL)
		{
			subLR->_parent = parent;
		}
		subL->_right = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subL;
		if (ppNode == NULL)
		{
			_root = subL;
			subL->_parent = NULL;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
				subL->_parent = ppNode;
			}
			else
			{
				ppNode->_right = subL;
				subL->_parent = ppNode;
			}
		}
		subL->_bf = parent->_bf = 0;
	}

	void _RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL= subR->_left;

		parent->_right = subRL;
		if (subRL != NULL)
		{
			subRL->_parent = parent;
		}

		subR->_left = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subR;
		if (ppNode == NULL)
		{
			_root = subR;
			subR->_parent = NULL;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
				subR->_parent = ppNode;
			}
			else
			{
				ppNode->_right = subR;
				subR->_parent = ppNode;
			}
		}
		subR->_bf = parent->_bf = 0;
	}

	void _RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL= subR->_left;
		int bf = subRL->_bf;

		_RotateR(parent->_right);
		_RotateL(parent);

		if (bf == 1) //从subRL的右边插入
		{
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1) //从subRL的左边插入
		{
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else    //(bf=0)
		{
			parent->_bf = 0;
			subR->_bf = 0;
		}
		subRL->_bf = 0;
	}
	void _RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		_RotateL(parent->_left);
		_RotateR(parent);

		if (bf == 1)
		{
			parent->_bf = 0;
			subL->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = 0;
		}
		else   //bf=0
		{
			parent->_bf = 0;
			subL->_bf = 0;
		}
		subLR->_bf = 0;
	}
	bool _IsBlance(Node* root)
	{
		if (root == NULL)
			return true;
		int right = _Height(root->_right);
		int left = _Height(root->_left);
		if (right - left != root->_bf || abs(right - left) >= 2)
		{
			cout << "平衡因子异常" << root->_key << endl;
			return false;
		}
		return _IsBlance(root->_left) && _IsBlance(root->_right);
	}
	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;
		int right = _Height(root->_right);
		int left = _Height(root->_left);
		if (right > left)
			return (right + 1);
		else
			return (left + 1);
	}
	void _InOrder(Node* root)
	{
		if (root == NULL)
		{
			return;
		}
		else
		{
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
	}
protected:
	Node* _root;
};

void Test1()
{
  cout << "Test1 : " << endl;
	int a[9] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	AVLTree<int, int> avl;
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
	{
		avl.Insert(a[i],i);
	}
	avl.InOrder();
	cout<<avl.IsBlance()<<endl;

	AVLTreeNode<int, int>* ret1 = avl.Find(18);
	if (ret1)
		cout << ret1->_key << ":" << ret1->_value << endl;
	else
		cout << "不存在ret1" << endl;

	AVLTreeNode<int, int>* ret2 = avl.Find(1);
	if (ret2)
		cout << ret2->_key << ":" << ret2->_value << endl;
	else
		cout << "不存在ret2" << endl;

	avl.Remove(26);
	avl.Remove(18);
	avl.Remove(15);
	avl.InOrder();

	avl.Remove(3);
	cout << avl.Remove(7) << endl;
	cout << avl.Remove(7) << endl;
	cout << avl.Remove(9) << endl;
	cout << avl.Remove(11) << endl;
	
	avl.InOrder();
	cout << avl.IsBlance() << endl;
	avl.InOrder();
	
	cout << avl.Remove(14) << endl;
	
	cout << avl.Remove(15) << endl;

	
	avl.Remove(9);
	avl.Remove(11);
	avl.Remove(14);
	avl.Remove(15);
	
	cout << avl.Remove(100) << endl;
	avl.Remove(16);
	avl.Remove(18);
	avl.Remove(26);

	avl.InOrder();
}

void Test2()
{
    cout << "Test2 : " << endl;
	int a[10] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> avl;
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
	{
		avl.Insert(a[i], i);
	}
	avl.InOrder();
	cout << avl.IsBlance() << endl;

	AVLTreeNode<int, int>* ret1 = avl.Find(5);
	if (ret1)
		cout << ret1->_key << ":" << ret1->_value << endl;
	else
		cout << "不存在ret1" << endl;

	AVLTreeNode<int, int>* ret2 = avl.Find(88);
	if (ret2)
		cout << ret2->_key << ":" << ret2->_value << endl;
	else
		cout << "不存在ret2" << endl;

	avl.Remove(14);
	avl.Remove(16);
	avl.Remove(7);
	avl.InOrder();

	avl.Remove(15);
	avl.Remove(6);
	avl.Remove(5);
	cout << avl.Remove(4) << endl;
	avl.Remove(4);
	avl.Remove(3);
	avl.Remove(2);
	avl.Remove(1);
	cout << avl.Remove(100) << endl;
	avl.Remove(7);
	avl.Remove(16);

	avl.InOrder();
}

int main()
{
	Test1();
	cout << endl;
	cout << endl;
	Test2();
	return 0;
}

分析

问题中阐述的两种运行方式分别对应着 debug 和 release 版本的结果,区别在于 release 版本会进行一定的编译优化。

说点题外话,我们再来回顾一个 C/C++ 程序从 .c 文件到 .exe的可执行文件的流程:

预处理,把宏替换
编译,转成汇编代码
汇编,转成二进制代码
链接,生成可执行文件
.c 文件
.i 文件
.s 文件
.o 文件
.exe 文件

其中,编译优化就是在汇编代码中优化一些不必要的操作。例如本代码的问题是出现了访问到已经被 delete 的内存,对于 debug 版本,在访问内存的指针中会通过 ebp 寄存器,而这种方式会允许少量数组越界或者访问非法内存的情况,进而能让程序继续运行;而对于 release 却会优化会省略 ebp 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃,这种指针又称 帧指针

解决

问题的关键是程序中,被删除的子节点的孩子节点的父亲指针没有正确设置,以及删除后其祖先节点的平衡因子没有调整导致,所修改 Remove 部分代码即可。

参考

  1. C++ debug和release版本的区别及调试技巧
  2. esp和ebp详解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小胡同的诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值