一个double free相关问题的澄清
引言
前一阵定位 Oracle 的 OCI 接口相关的一个内存释放问题,在网上看到了链接如下的这篇文章:
一个C++bug引入的许多知识
看到后面说 vector 里的两个单元里的内部成员指针地址是一样的,感觉很怪异。于是写了个简单程序实际验证一下。
COuter 和 CInner 类
1 #include <stdio.h>
2 #include <vector>
3
4 class CInner
5 {
6 public:
7 CInner(int id) : m_nId(id) {
8 printf(" Inner item %d constructor\n", id);
9 }
10 ~CInner() {
11 printf(" Inner item 0x%X deconstructor\n", m_nId);
12 }
13 private:
14 int m_nId;
15 };
16
17 class COuter
18 {
19 public:
20 COuter(int id) : m_pInner(NULL), m_nId(id) {
21 printf("Outer item %d constructor\n", id);
22 }
23 ~COuter() {
24 printf("Outer item %d deconstructor\n", m_nId);
25 if (m_pInner) {
26 printf("Inner item %d with address=0x%X will be deleted...\n", m_nId, m_pInner);
27 delete m_pInner;
28 printf("Inner item %d deleting done.\n\n", m_nId);
29 }
30 else {
31 printf("Item %d has no inner item.\n\n", m_nId);
32 }
33 }
34
35 CInner* getInnerItem() {
36 if (NULL == m_pInner) {
37 m_pInner = new CInner(m_nId);
38 printf("A inner item with id=%d and address=0x%X is created.\n", m_nId, m_pInner);
39 }
40 return m_pInner;
41 }
42 private:
43 CInner* m_pInner;
44 int m_nId;
45 };
main
1 int main()
2 {
3 std::vector<COuter> vec;
4 for (int idx = 0; idx < 2; ++idx) {
5 COuter item(idx);
6 printf("Outer item %d will be pushed into vector...\n", idx);
7 vec.push_back(item);
8 printf("Outer item %d is in vector.\n", idx);
9 vec.back().getInnerItem();
10 }
11 return 0;
12 }
运行结果分析
程序本身很简单,以下以 Windows 平台为例(Linux 下情形类似)就运行输出结果进行分析。
程序运行输出以下信息后报错:
1 Outer item 0 constructor // 局部变量COuter item(0)开始生命周期,其构造函数被调用,这是第一个id为0的Outer实例
2 Outer item 0 will be pushed into vector...
3 Outer item 0 is in vector. // 此时有两个Outer实例,id都为0,第二个是第一个的拷贝构造,并已放入vec里
4 Inner item 0 constructor // 为第二个Outer实例构造inner实例
5 A inner item with id=0 and address=0xD55D98 is created.
6 Outer item 0 deconstructor // COuter item(0)到了生命周期,其析构函数被调用,之后只剩第二个id为0的Outer实例
7 Item 0 has no inner item.
8
9 Outer item 1 constructor // 局部变量COuter item(1)开始生命周期
10 Outer item 1 will be pushed into vector...
11 Outer item 0 deconstructor
12 Inner item 0 with address=0xD55D98 will be deleted...
13 Inner item 0x0 deconstructor
14 Inner item 0 deleting done.
15
16 Outer item 1 is in vector.
17 Inner item 1 constructor
18 A inner item with id=1 and address=0xD55D50 is created.
19 Outer item 1 deconstructor
20 Item 1 has no inner item.
21
22 Outer item 0 deconstructor
23 Inner item 0 with address=0xD55D98 will be deleted...
24 Inner item 0xFEEEFEEE deconstructor
第11行到第14行的输出信息说明:vec里因为要增加新单元,其内存空间要做调整,第三个id为0的Outer实例由第二个id为0的Outer实例拷贝构造而成并加入vec,随后便释放了第二个id为0的Outer实例。由于缺省拷贝函数会对指针成员做简单值拷贝,第三个id为0的Outer实例里的m_pInner指针指向的是第二个id为0的Outer实例所指向的Inner实例,即两个Outer实例内部指向了同一个Inner实例。而这个Inner实例随着第二个id为0的Outer实例的释放而被释放了,第三个id为0的实例还在指向这个被释放了的Inner实例,这就留下了隐患,因为。
第16行到20行的输出,很好理解,此时vec里有两个单元,其中id为1的单元实例所指向的Inner实例也已创建好了。两个Inner实例的所占堆空间的起始地址是不一样的,即0xD55D98和0xD55D50。问题在于0xD55D98所指向的地址已经释放过了。
第22行的输出说明:局部变量vec到了生命周期,在释放第一个单元实例时,由于其指针成员不为NULL,于是对0xD55D98所指空间又做一次释放,从而引发了问题。
一个double free相关问题的澄清的更多相关文章
- ZOJ Problem Set - 1331 Perfect Cubes 判断一个double是否为整数
zju对时间要求比较高,这就要求我们不能简单地暴力求解(三个循环搞定),就要换个思路:因为在循环时,已知a,确定b,c,d,在外重两层循环中已经给定了b和c,我们就不用遍历d,我们可以利用d^3=a^ ...
- 给定一个double类型的数组arr,其中的元素可正可负可0,返回子数组累乘的最大乘积。例如arr=[-2.5,4,0,3,0.5,8,-1],子数组[3,0.5,8]累乘可以获得最大的乘积12,所以返回12。
分析,是一个dp的题目, 设f[i]表示以i为结尾的最大值,g[i]表示以i结尾的最小值,那么 f[i+1] = max{f[i]*arr[i+1], g[i]*arr[i+1],arr[i+1]} ...
- java定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积
需求如下:(1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积. (2)定义一个类PassObject,在类中定义一个方法pri ...
- impala 四舍五入后转换成string后又变成一个double的数值解决(除不尽的情况)
impala 四舍五入后转换成string后又变成一个double的数值解决(除不尽的情况)例如Query: select cast(round(2 / 3, 4)*100 as string)+-- ...
- 记一个界面刷新相关的Bug
今天遇到一个比较有意思的bug, 这里简单记录下. Bug的症状是通过拖拉边框把我们客户端主窗口拖小之后,再最大化,会发现窗口显示有问题, 看起来像是刷新问题, 有些地方显示的不对了. 这里要说明的是 ...
- 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
// test14.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...
- 汇编入门——使用DOSBox写一个HelloWorld以及相关软件安装
0.0.0) 在D盘建立一个ASM文件夹 0.0.1) 放入所需要的文件 1所标示的红色框为必须要存在的文件,要处理汇编文件.百度网盘中下载. 2自己编写的汇编(asm)文件. 3编译汇编自己生成的文 ...
- java如何获取一个double的小数位数
前言 看标题是不是觉得这是一个很简单的问题,我一开始也是这么认为的,但是实际情况下,在各种情况下我们都进行了测试,发现很多实际情况是无法不尽如人意的. 方法分析 当前能想到的比较容易有下面几种 1.直 ...
- 分享一个 UiPath Studio 相关的公众号
RPA 和 UiPath 方面的资料比较少,因此我们自己创建了一个公众号,专门用于传播 UiPath 相关的知识. 会定期发布 UiPath 学习相关的信息.是目前难得的 UiPath 中文资源. 公 ...
随机推荐
- Socket介绍(五)
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接 ...
- odoo里面的一些ORM操作
案例0001sale_obj=self.env['sale.order'].browse(k)通过browse查找对象找到对象里面的 sale_obj.name browse 获取一个数据库id或一个 ...
- 用Pygal画一个英雄能力的图
效果图: 这是在google浏览器下打开的html. play.py #!/usr/bin/env python # _*_ coding: UTF-8 _*_ """= ...
- 第三十二篇 -- CreateFile、ReadFile、WriteFile
一.CreateFile 这是一个多功能的函数,可打开或创建文件或者I/O设备,并返回可访问的句柄:控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道. 函数原型: HANDLE WIN ...
- Qt 入门 ---- 布局管理
这是运行后的程序界面: 这是点击右上角"最大化"之后的程序界面: 接下来讲一下如何进行自动布局解决窗口拉伸问题. ① 原理: 在项目"设计"模式的左侧有如下两个 ...
- Chrome添加 测试设备 手机iPhone x系列手机
一.F12,点击下图中的Edit,添加设备 二.添加名为iPhone XR的设备,设备配置如下 三.其中User agent string如下 Mozilla/5.0 (iPhone; CPU iPh ...
- linux之frp服务部署(内网穿透)
frp服务部署(内网穿透) 目的 更快的进行内网穿透调试以及云端开发测试 服务器为CentOS 7,客户端为win11 frp介绍 frp 是一个开源项目, 采用 C/S 模式,将服务端部署在具有公网 ...
- [Ynoi2011]初始化 题解
第一道Ynoi,纪念一下. 众所周知,Ynoi会进行惨无人道的卡常操作,所以我们可以使用暴力去做Ynoi. 于是乎,我们考虑分块+暴力. 对于操作2,不难发现是道裸的分块,可以抄P3372的代码. 对 ...
- 数据增广imgaug库的使用
记录一下这两天用imgaug库做数据增广的代码,由于是算用算学的,所以只能把代码写出来,具体每种增广算法的原理和一些参数就不得而知了,不过我觉得也没必要把这么些个算法搜搞懂,毕竟重点是扩种数据.所以, ...
- Build a Beautiful oh-my-zsh Themes
Selection Criteria double line; provide username, hostname, current directory; provide information o ...