1. 起因

某天,一个同事跟我反馈说在windows上调试公司产品的一个交易核心时出现了使用未初始化的指针导致后台服务崩溃的情况。示例代码如下所示:

 struct sample
{
int* ptr_table[][];
//... other members
}; void test()
{
sample* sample_ptr = new sample[];
for (int i = ; i < ; i++)
sample_ptr[].ptr_table[][i] = new int(i); // 实际系统中是根据初始化数据对sample_ptr数组中的对象进行赋值,但不是所有的对象都有初始化数据;
int* int_ptr = sample_ptr[].ptr_table[][];
if (int_ptr != NULL)
{
printf("ptr1 = 0x%x\n", int_ptr);
*int_ptr = ;
} int_ptr = sample_ptr[].ptr_table[][];
if (int_ptr != NULL)
{
printf("ptr2 = 0x%x\n", int_ptr);
*int_ptr = 100; // crashed here!
}
}

使用未初始化的指针是c++的大忌,但是该代码在产品发布2年左右的时间一直没有出现过问题。唯一的区别是发布运行环境是linux,而调试环境是最近才配置好的windows环境。分别在linux下和windows下对代码进行debug,发现linux下新申请的内存被初始化为0,而windows下新申请的内存却并未初始化。

将sample* sample_ptr = new sample[10]这行改为sample* sample_ptr = new sample[10]()后两个系统执行的结果变一样了,都是被初始化的内存。

那么问题来了:

(1) 为什么相同的代码(new sample[10])在两个系统下表现形式不一样呢?是两个系统的内存分配机制的原因还是类库的原因?

(2) new sample[10]和new sample[10]()的区别到底是什么?

2. 研究

在c++中,一般都是以new/delete来申请和释放内存。对于以下几种new的用法,各自的区别是什么呢?

 int* p1 = new int;
int* p2 = new int();
int* p3 = new int();
// define class A;
A* p4 = new A;
A* p5 = new A();
A* p6 = new A[];
A* p7 = new A[]();

在平时写代码的时候,对于一块新申请的内存我都会要对它进行初始化后才会去用它,一般是例如p3的直接初始化或者memset,因此对于new A和new A()两种用法的结果还真是不了解之间的区别。

根据《C++ Primer, Fourth Edition》中5.11节[1] The new and delete Expressions中关于new的描述,new A属于Default Initializing of Dynamically Allocated Objects(动态创建对象的默认初始化),而new A()则属于Value Initializing of Dynamically Allocated Objects(动态创建对象的值初始化)。

当为默认初始化操作时,若被创建的对象没有显式定义默认构造函数,则按照2.3.4节[2]Variable Initialization Rules的规则进行初始化:

  • 对象为内置类型时,任何在函数体外定义的变量都会被初始化0(全局变量或者静态变量),在函数体内定义的变量都不会进行初始化
  • 对象为类类型时,调用对象的默认构造函数

当为值初始化操作时,若被创建的对象没有显式定义默认构造函数,则认为对该对象进行初始化操作。

不同new的用法对应的初始化的逻辑总结如下:

  new A new A() new A(parameters)
A为内置类型 无初始化动作

进行值初始化

例A为int类型,则初始化为0

进行值初始化,A被初始化为parameters
A为calss/struct

调用默认构造函数,A中成员是否

初始化依赖于默认构造函数的实现

若自定义了默认构造函数,则调用自定义的默认构造函数。

否则调用系统默认构造函数,并对A中的成员进行值初始化

调用A的自定义构造函数

因此上面代码的执行结果分别为:

  • p1指向了一个未被初始化的int空间
  • p2指向了一个被初始化的int空间,其值为0
  • p3指向了一个被初始化的int空间,其值为1
  • p4指向了一个调用了默认构造函数的实例A,除非A的默认构造函数对A的成员进行初始化,否则A的成员全为未初始化变量
  • p5指向了一个调用了默认构造函数的实例A,若A自定义了默认构造函数,A成员变量的初始化依赖于自定义的默认构造函数,反之A的成员变量全为初始化后的变量
  • p6、p7指向了调用了默认构造函数的实例A的数组,其执行结果同p4、p5

回到之前的问题,结构体sample是没有自定义默认构造函数的,按照c++标准,new sample的执行结果是sample中的ptr_table是不会被初始化的。这个在windows上是一致的,可是在linux上为什么被初始化成了0了呢?在stackoverflow上找到了一个类似的问题[3],其答案为:“Memory coming from the OS will be zeroed for security reasons....the C standard says nothing about this. This is strictly an OS behavior. So this zeroing may or may not be present on systems where security is not a concern”。操作系统的安全机制会在用户程序申请内存时对分配的内存进行初始化,以防止别有用心的人从新申请到的内存中读取到敏感数据,例如密码等,正是由于这个linux的特性使程序在首次申请内存时总是能得到一块被初始化的内存。

关于这个安全机制我在网上并没有搜到比较官方的说明文档,也许是我的搜索方式有误或者没有找对关键词,而且发现在程序运行过程中申请的内存也总并不是被初始化了的内存,例如下面的代码:

 int size = ;
{
int * p1 = new int[size];
for (int i = ; i < size; i++)
{
p1[i] = i + ;
printf("%d\t", p1[i]);
}
printf("\n");
delete []p1;
} {
int * p1 = new int[size];
for (int i = ; i < size; i++)
printf("%d\t", p1[i]);
delete []p1;
}
printf("\n");

执行结果:

希望对这方面有了解的大神能提供一下相关的资料。

3. 总结

(1) 项目中的代码是明显不符合c++代码规范的,在逻辑上会存在使用未经初始化的指针的现象。个人认为变量的初始化不应依赖于编译器或者系统的实现,而是尽量遵照c++标准或者手工初始化。

(2) 针对class/struct类型,如果没有自定义默认构造函数,不同的new的用法会产生不同的结果,这个在以后写代码的时候要注意。

1:http://shouce.jb51.net/c++/0201721481/ch05lev1sec11.html

2.  http://shouce.jb51.net/c++/0201721481/ch02lev1sec3.html#ch02lev2sec13

3:http://stackoverflow.com/questions/8029584/why-does-malloc-initialize-the-values-to-0-in-gcc

本文为原创内容,若有错误的地方烦请指正

本文地址:http://www.cnblogs.com/morebread/p/4936441.html

由一次程序崩溃引起的对new表达式的再次学习的更多相关文章

  1. iOS-----App闪退,程序崩溃---解决方案

    1.iOS-中app启动闪退的原因 2.iOS开发-闪退问题-解决之前上架的 App 在 iOS 9 会闪退问题 3.iOS-应用闪退总结 4.iOS开发-捕获程序崩溃日志 5.iOS开发-应用崩溃日 ...

  2. iOS - 捕获应用程序崩溃日志

    作为一名iOS移动应用开发者,为了确保你的应用程序正确无误,在将应用程序提交到应用商店之前,你必定会进行大量的测试工作:而且在你测试的过程中应用程序运行的很好,但是在应用商店上线之后,还是有用户抱怨应 ...

  3. iOS开发-捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时 ...

  4. android 连接蓝牙扫码枪,程序崩溃之onConfigurationChanged

    当android手机通过蓝牙连接扫码枪时,程序崩溃的原因之一是:键盘弹出或隐藏,触发程序走了onDestory->onCreate的生命周期,从而可能使得页面的某些初始化数据被清除了. 解决方法 ...

  5. Android 程序崩溃后的处理

    在应用发布以后,由于安卓机型的千差万别 ,可能会出现各种各样的问题,这时候如果我们可以将这些信息收集起来,并进行修改就很不错了.下面就来讨论一下怎么处理程序崩溃以后,错误信息的手机. Java中已经提 ...

  6. iOS 中捕获程序崩溃日志

    iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59) 转载▼     iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下 ...

  7. 让linux中的程序崩溃时生成core文件

    当我们的linux程序崩溃的时候,常常会有这样的提示:    Segmentation fault (core dumped)    段错误 (核心已转储)    提示说生成了core文件,但是此功能 ...

  8. 水火难容:同步方法调用async方法引发的ASP.NET应用程序崩溃

    之前只知道在同步方法中调用异步(async)方法时,如果用.Result等待调用结果,会造成线程死锁(deadlock).自己也吃过这个苦头,详见等到花儿也谢了的await. 昨天一个偶然的情况,造成 ...

  9. setSupportActionBar(toolbar)导致程序崩溃闪退

    最近在做一个项目,使用了第三方的开源项目,主要是想实现android5.0之后推出的MaterialDesign的风格,但是代码已经写好了,发现一运行就闪退,所以就开始debug,发现问题出现在 To ...

随机推荐

  1. 举例讲解Linux系统下Python调用系统Shell的方法

    有时候难免需要直接调用Shell命令来完成一些比较简单的操作,比如mount一个文件系统之类的.那么我们使用Python如何调用Linux的Shell命令?下面来介绍几种常用的方法:1. os 模块 ...

  2. 解决Python2.7的UnicodeEncodeError: 'ascii' codec can’t encode异常错误

    import sys reload(sys) sys.setdefaultencoding('utf-8') 好了,通过上面短短的三行,我们算是很好的解决了这个问题了,同样的方式也可以应用到Unico ...

  3. jmeter 性能测试工具的使用(Web性能测试)

    1.下载 http://pan.baidu.com/s/1o7p18Ye 该软件不用安装,直接解压打开即可使用. 2.使用 这里就在win下进行,图形界面较为方便 在目录apache-jmeter-2 ...

  4. 常用的JavaScript模式

    模式是解决或者避免一些问题的方案. 在JavaScript中,会用到一些常用的编码模式.下面就列出了一些常用的JavaScript编码模式,有的模式是为了解决特定的问题,有的则是帮助我们避免一些Jav ...

  5. 周五了啦啦啦啦-LAMP+PHP‘s OOP

    hi 周五咯~~ 1.LAMP配置完结篇 五.LAMP配置环境优化 5.4 虚拟主机工作原理 apache的虚拟主机.virtual-host 用不同的域名访问不同的目录——手动模拟dns 修改hos ...

  6. 使用dedecms中常见错误提示及解决办法(一)

    在使用 dedecms 做网站时,常常会遇到一些棘手的问题,比如:页面图片不显示(src 的地址不对)等等. 1. 更新网站时错误 问题:Call to a member function GetIn ...

  7. Eclipse较为常用快捷键

    今天在学习Eclipse的使用时,Mark了一些较为常用的快捷键,拿出来和大家分享一下: Ctrl+1 快捷修复 Ctrl+D 快捷删除行 Shift+Enter 在当前行任意位置光标跳转到下一行 C ...

  8. HDU3535AreYouBusy[混合背包 分组背包]

    AreYouBusy Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. AC日记——字符串P型编码 openjudge 1.7 31

    31:字符串p型编码 总时间限制:  1000ms 内存限制:  65536kB 描述 给定一个完全由数字字符('0','1','2',…,'9')构成的字符串str,请写出str的p型编码串.例如: ...

  10. Android SDK和N多Android开发资源

    开发资源 本文收集整理Android开发需要的Android SDK.工具.Android开发教程.Android设计规范,免费的设计素材等. 欢迎大家推荐自己在Android开发过程中用的好用的工具 ...