我的编程乐园
积累,坚持!
---------我是一只IT小小鸟
首页
新随笔
联系
聚合
管理
随笔-145 评论-173 文章-70 trackbacks-0
【欢迎各位留言讨论】C++中运算符New的一个疑问【新的问题!】【各位继续关注讨论啊!】
最近突然发现自己曾经建立的C++体系出现了很多漏洞,对很多问题都产生了疑问,不知道自己是钻了牛角尖还是探究C++的底层或者细节,不过既然碰到了,就解决之吧,或许在某一天之后,会惊奇的发现,原来自己的这些问题会出现某天的笔试或者工作中呢。
言归正传,这个问题是关于new操作符的。今天在完成C++作业的时候,需要自己实现一个strlen来,其实就是调用C语言函数就可以,不过我突发奇想,写下了下面的这个代码:
code 1:
1
#include
<
iostream
>
2
using
namespace
std;
3
4
int
main()
5
{
6
char
*
str
=
new
char
[
4
];
7
strcpy(str,
"
fjifejfief
"
);
8
cout
<<
str
<<
endl;
9
}
code 2:
1
#include
<
iostream
>
2
using
namespace
std;
3
4
int
main()
5
{
6
char
*
str
=
new
char
[
5
];
7
strcpy(str,
"
Good
"
);
8
strcat(str,
"
I Love C++
"
);
9
cout
<<
str
<<
endl;
10
}
上面的两个代码都是用到了new操作符,动态分配了一个固定的存储空间,OK。
然后通过strcpy来实现赋值,从而将原来的那个分配的区域填充。可是在1和2中都有一个问题,那就是,填充的字符串超过了动态分配的大小,在code1中,实际分配的只有4个字节,而自己拷贝过去的确有10个字节(不含null),而code2中,首先初始化的时候没有问题,后面的连接函数也是。为何我会产生这个问题呢?
因为曾经在《C++ Primer》一书中讲到,当进行字符串操作的时候,需要特别注意到那个null要占用一个空间,而对于strcat和其他函数,特别要注意是否超过表示的范围,而我自己曾经碰到过这个问题,所以比较疑惑,上面的那个没有溢出吗?为何可以输出正确的结果呢?
下面,列出个错误的例子来看看:
code3
1
#include
<
iostream
>
2
using
namespace
std;
3
4
int
main()
5
{
6
char
str[
4
]
=
"
goo
"
;
7
cout
<<
str
<<
endl;
8
strcat(str,
"
sfsfefe
"
);
9
cout
<<
str
<<
endl;
10
return
0
;
11
}
明显code3会出错(刚开始的时候居然没有出错,后来程序才崩溃的,以为灵异事件呢。),固定分配的数组的话,需要特别注意像strcat函数,因为这种连接函数就可能超过范围,发生错误。
ok:
汇总如下:固定分配的时候,不能够超过数组的范围,而且字符串的话需要注意总是有一个null在其中的,所以大小要自己把握好。
但是,对于new动态分配的话,可以超过范围,不管是strcat还是直接在分配的时候超过范围都可以,Why?
============================================================================================================
经过网友留言汇总,同时自己测试,发现了一些新的问题,也有了新的体会! Version 1
============================================================================================================
留言汇总(解答):
经过和网友的讨论,我初步了解了错误可能的原因,现罗列如下,如有遗漏和错误,还望各位指教。
1.两者的分配方式不同。使用关键字new操作符分配的话,分配的空间是在堆当中(heap),而直接使用数组的话,分配的空间在堆栈中(stack)。
2.操作。对于堆栈的溢出,由于变量的填充时按照从高地址到低地址的方式,所以,溢出的话会修改函数的返回值,造成运行的错误。而对于堆分配的话,这里即使溢出了,由于没有对它进行进一步操作,所以没有出现问题。
3
.我的一点理解。对于堆栈溢出的错误,我想大家都知道了,可是上面的这个堆溢出的话,好像即使有错误也没有什么问题。我尝试了使用
delete []str,或者是输出str[8]等单元,都是显示正确的结果,也没有出现错误。所以怀疑的是,难道堆上的这种分配这么安全,那我delete的话,到底是删除的分配的4个字节,还是拷贝过去的溢出的那个超过4个字节的那么多单元内容呢?我的理解是,堆上分配的单元由于太随意,灵活性太强,所以对于错误的发生就可能性很小。
4.
我写的调试代码的一个新的问题:
1
#include
<
iostream
>
2
using
namespace
std;
3
4
int
main()
5
{
6
char
*
str1
=
new
char
[
4
];
7
str1
=
"
sfe
"
;
//如果去掉这一行,那么程序就可以正常运行了……可是这个仅仅是初始化指针的啊。
8
char
str2[
4
]
=
"
adf
"
;
9
cout
<<
"
赋值前:
"
<<
endl;
10
cout
<<
"
str1指向:
"
<<&
str1
<<
endl;
11
cout
<<
"
str1内容:
"
<<
str1
<<
endl;
12
cout
<<
"
str2指向:
"
<<&
str2
<<
endl;
13
cout
<<
"
str2内容:
"
<<
str2
<<
endl;
14
strcpy(str2,
"
ooo
"
);
15
cout
<<
"
str2先赋值后:
"
<<
endl;
16
cout
<<
"
str1指向:
"
<<&
str1
<<
endl;
17
cout
<<
"
str1内容:
"
<<
str1
<<
endl;
18
cout
<<
"
str2指向:
"
<<&
str2
<<
endl;
19
cout
<<
"
str2内容:
"
<<
str2
<<
endl;
20
strcpy(str1,
"
iiiiii
"
);
21
cout
<<
"
str1后赋值后:
"
<<
endl;
22
cout
<<
"
str1指向:
"
<<&
str1
<<
endl;
23
cout
<<
"
str1内容:
"
<<
str1
<<
endl;
24
cout
<<
"
str2指向:
"
<<&
str2
<<
endl;
25
cout
<<
"
str2内容:
"
<<
str2
<<
endl;
26
27
}
28
程序在运行一半后崩溃,典型的溢出了。但是,此代码如果去掉上面初始化的那一行,程序就没有错误,而且我这里还计算发现对于堆栈的话没有任何错误,而堆的话故意写了个溢出的赋值,就是下面strcpy(str1,"iiiiii");
此时加上初始化的代码后,问题就来了,上个截图。
从错误的信息来看,
就是指向到 strcpy(str1,"iiiiii")的时候出错了的。
这个说明:在我没有对堆上的变量初始化的时候,如果越界了,没有发现出错(它会自动扩展那个大小吗?)。
而如果我对堆上的变量初始化的话,那么,再次复制的时候,如果越界了,就会发生错误!
各位,知道为什么吗?(
问题好像更明朗了些,而且好像更深入了些。当然,钻这个牛角尖或许没有必要!)
posted on 2010-01-13 23:14
deercoder
阅读(2182)
评论(23)
编辑
收藏
引用
评论:
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问[未登录] 2010-01-14 00:54 |
Steven
你需要去了解下 堆(stack) 和 栈(heap) 的区别.
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 02:06 |
陈梓瀚(vczh)
因为new的时候,系统可以选择多给你几个字节,而且这并不是固定的数字。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 02:20 |
OwnWaterloo
1. 在C/C++代码正确且C/C++实现(编译器,运行库)正确的情况下, C/C++语言保证得到正确的结果。
如果出现了错误的结果, 可以问why。
是"C/C++代码正确"这个假设有误? 还是"C/C++实现正确"这个假设有误?
2. 如果C/C++代码本身有某种错误, C/C++实现不保证得到正确的结果, 也不保证得到错误的结果, 更不保证会报告错误的结果。
代码的错误有可能会被隐藏, 到其他时候发作。
这时候, 询问"为什么没有出现错误", 是不明智的。
C/C++实现没有义务保证产生一个错误。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 09:22 |
bluegene
我是这样理解的,你用 new 分配的空间是放在堆(heap)里的,超出了的空间如果不被其它数据覆盖暂时是没有问题的。固定分配的时候是在栈(stack)里分配的,其空间肯定是不能益处的。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 10:26 |
饭中淹
new分配的在堆里,你溢出了,只会导致堆出问题。而你下面又没有再使用堆,所以不会出错。
直接写的局部变量,分配在堆栈里,堆栈里有函数的返回地址,所以溢出了,覆盖了返回地址,函数执行完就出错了。
另外,有些 编译器会生成对固定长度数组的保护性检测代码,一旦溢出,就会弹出提示。早先在vs2003还是vs2002的时候见过。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 10:27 |
zuhd
对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。看下反汇编代码一切都明白了
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 10:31 |
zuhd
栈里有函数的返回地址,所以溢出了,覆盖了返回地址,函数执行完就出错了。
==========================================
这句话是重点,当ret的时候,call下条指令时异常了,如果你多定义了几个变量,让栈溢出不到函数的返回地址,错误依然不会出现的
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问[未登录] 2010-01-14 12:43 |
vane
出错是必然的,没问题的时候你比较幸运。
#include <iostream>
using namespace std;
#pragma pack(push)
#pragma pack(1)
int main()
{
char *str = new char[5];
strcpy(str,"Good");
strcat(str,"I Love C++");
cout << str << endl;
}
#pragma pack(pop)
你可以这么试一下
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 15:53 |
坏人
越界,后果自负。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 15:57 |
turygo
数组越界都是这样的,错误不是立刻出现,而是不知道什么时候就出问题了,而且到那个时候代码量一大,你根本无从找起错误。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 17:30 |
零宇
建议楼主补充一下基础知识
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 19:22 |
besterChen
LZ遇到的问题是堆和栈溢出的问题,您说的可能是灵异事件是因为程序对栈和堆检查导致的。
不知道LZ用的是什么开发环境。我学习C语言也刚好学到这里,自己在VC6得开发环境下仔细调试过其过程并做了笔记,地址:
http://www.cppblog.com/besterChen/archive/2010/01/13/105538.html
希望能对楼主有帮助,由于自己也是初学,希望LZ能多多指正日志中的错误……
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 22:30 |
wildpointer
这种越界的事,你知道会错还用。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 23:21 |
刘畅
@Steven
好的,谢谢。我觉得也应该是这方面的问题。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 23:22 |
刘畅
@陈梓瀚(vczh)
可是多出来的字节是无法预知的啊。而且如果太多的话还是会溢出的吧。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 23:25 |
刘畅
@bluegene
谢谢,之前一直觉得和内存的分配有关,堆栈和堆确实不大一样。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 23:27 |
刘畅
@饭中淹
@zuhd
谢谢你们的精彩解释,受益匪浅。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 23:29 |
刘畅
@besterChen
呵呵,一起学习!
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-14 23:29 |
刘畅
@零宇
好的,谢谢!
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-15 00:05 |
Benjamin
strcpy等在一些公司的编码规范中是禁用的。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问 2010-01-15 00:55 |
空明流转
bushuoshale...
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问[未登录] 2010-01-15 09:47 |
Steven
@刘畅
呵呵,我把堆和栈的英文写反了。。。
以前写C 程序的时候经常用到大量的 strcopy, strcat之类的函数,偶尔出错是在所难免的,只要是人总是有算错的时候,尤其有些时候混合处理unicode和ansi串的时候,算字符串长度是容易出错的。
所以后来写 C++ 的时候我尽量避免去使用这类函数了,而是倾向于用 stl的string wstring, 尽量用 stl的容器类去代替自己分配管理内存。
代码规模大到一定程度,除了缓冲溢出实在是很难找。
顺便说下,
http://www.coverity.com/products/static-analysis.html
这个东西很强大,可以检测到你代码里潜在的溢出问题。
回复
更多评论
#
re: 【欢迎各位留言讨论】C++中运算符New的一个疑问
2010-01-15 12:43 |
刘畅
@Steven
谢谢,去看看,学习了……
回复
更多评论
刷新评论列表
只有注册用户
登录
后才能发表评论。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
网站导航:
博客园
IT新闻
BlogJava
知识库
博问
管理
坚持记录,笔耕不辍,笔记是最好的学习方法!
<
2010年1月
>
日
一
二
三
四
五
六
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
常用链接
我的随笔
我的评论
我参与的随笔
留言簿
(12)
给我留言
查看公开留言
查看私人留言
随笔分类
(87)
ACM(1)
Android(4)
C++(7)
CTeX和LateX(1)
Git(3)
Java(4)
MFC程序设计入门(8)
OpenCV(1)
Python(3)
Shell/Bash(1)
SQL(1)
Unix/Linux(23)
Vim(3)
大学公开课(3)
读书(4)
环境配置(1)
生活感悟/日记(18)
图像识别算法及原理(1)
随笔档案
(145)
2014年12月 (1)
2013年3月 (1)
2012年7月 (2)
2012年6月 (5)
2012年5月 (2)
2012年4月 (2)
2011年12月 (1)
2011年11月 (2)
2011年10月 (8)
2011年9月 (2)
2011年8月 (4)
2011年6月 (2)
2011年5月 (5)
2011年4月 (3)
2011年3月 (3)
2010年6月 (5)
2010年5月 (5)
2010年4月 (3)
2010年3月 (16)
2010年2月 (56)
2010年1月 (11)
2009年12月 (2)
2009年11月 (3)
2009年10月 (1)
文章分类
(70)
C/C++(14)
JAVA(6)
Linux/Unix(6)
MFC(5)
OpenCV / OpenGL(6)
编程体会和收获(3)
常见编译器错误解决办法(5)
深入理解计算机系统(2)
生活的体会和感悟(5)
实习/读研(1)
数据结构和算法分析(9)
杂谈(8)
文章档案
(70)
2011年11月 (1)
2011年10月 (1)
2010年3月 (8)
2010年2月 (2)
2010年1月 (3)
2009年12月 (21)
2009年11月 (26)
2009年10月 (5)
2009年9月 (3)
相册
computer picture
ACM与算法比赛
Google Code Jam
Top Coder
北大ACM
杭电ACM
LaTex和Tex学习
LaTex and Tex
Tex,LaTex,CTex学习
电子书下载
不错的电子书免注册下载
杂志下载(经济学人等)
联系方式
我的豆瓣主页
学习论坛
C++编程
VC知识库
超多C/C++资料和源码下载
科研小木虫
提问必答网站(牛人辈出啊!)
英语网站(长期学习)
New York Times
华尔街日报
记单词,捐大米
经济学英文网
普特网站
普特英语应用(有趣的学习)
译言网|译文库
中国日报
源码网站
codeproject
google代码搜索
programersheaven
sourceforge
程序员联合开发网
最新随笔
1. 此博客停止更新
2. Adboe Reader提示中文字体有问题
3. Python字符串换行处理
4. 如何转换^M行末符号
5. 斯坦福大学开放课程--编程范式(四)
搜索
积分与排名
积分 - 899071
排名 - 15
最新随笔
1. 此博客停止更新
2. Adboe Reader提示中文字体有问题
3. Python字符串换行处理
4. 如何转换^M行末符号
5. 斯坦福大学开放课程--编程范式(四)
最新评论
1. re: Git Stash用法[未登录]
@陈梓瀚(vczh)
人称轮带逛!!!
--q
2. re: Git Stash用法
@Loaden
这个B装的好
--doubi
3. re: Chrome神器Vimium快捷键学习记录
哦啦啦啦啦
--阿里河
4. re: Chrome神器Vimium快捷键学习记录
希望能添加更新后的功能翻译
--Vi.Ci
5. re: Chrome神器Vimium快捷键学习记录
@coolbit
谢谢,学会了
--xin
阅读排行榜
1. Git Stash用法(300425)
2. Chrome神器Vimium快捷键学习记录(67426)
3. GitHub使用简介(35318)
4. Ubuntu下硬盘的自动挂载(23711)
5. Ubuntu更新包管理器失败:Requires installation of untrusted packages问题解决(18436)
评论排行榜
1. 【欢迎各位留言讨论】C++中运算符New的一个疑问【新的问题!】【各位继续关注讨论啊!】(23)
2. Git Stash用法(21)
3. Chrome神器Vimium快捷键学习记录(19)
4. C++友元的一个问题-----------由派生类访问基类的私有成员(10)
5. OpenCV学习笔记(一)(7)