先看个题目:
void fun(char *p1)
{
p1=p1+1;}
main()
{
char *p="abcd";
fun(p);
cout<<*p;
}
这个题目看起来非常简单,但对于初学者来说却是个巨大的陷阱.很多初学者在学习c的时候就会听老师说过:指针是可以用来传值,甚至可以不要return;这句话本身没有错误,错的是初学者的理解!
首先说一下答案:结果是a不是b
这也许出乎你的意料.但是仔细一想就明白了.首先定义了一个字符型指针p,让它指向一个字符串的首地址,如果没有fun函数,那么*p='a';但是现在调用了fun,难道对p没有影响?
是的.调用fun的时候,会将p的值传给p1,也就是说现在p1也指向这个字符串的首地址了.然后执行:p1=p1+1;这样p1会指向下一个字符,也就是'b',但是p还是指向首字符!!!
所以结果也就出来了.这时候你可能会怀疑你以前学的指针是不是出现了问题.再来看这个题目,你就明白了:
void fun(char *p1)
{
*p1=*p1+2;
}
主函数不变,结果是'c'(a+2),同样p1开始指向了首地址,就当作一个箱子,然后把箱子里的东西换了(*p1),等调用结束以后,下个人(主函数)再来看的时候,箱子里的东西已经变化了!
总之,希望初学者在学习的时候多加琢磨一些关键的话语,这对你的分析能力是很有帮助的.
2.
main()
{
char p[]={'a','b','c'};
char q[]="abc";
printf("%d,%d,%d,%d",strlen(p),strlen(q),sizeof(p),sizeof(q));
}
感兴趣的读者,你能在不上机的情况下说出结果吗?
答案 是3,3,3,4.
我们知道strlen测的 是字符串的实际长度,不包括'\0',而sizeof测的是开始分配的空间.对于char q[]="abc";实际上应该是q[4],而对于char p[]={'a','b','c'};这种写法在很多C语言书上是不允许的,因为没有结束符.所以它的结果可能会出乎意料.在此不多作申明.不过如果写成char p[]={'a','b','c','\0'};那么sizeof(p)=4
3.new的问题
初学者都知道new是用来动态分配空间的,可以不用初始化,而直接赋值.如:
int *p=new int;
*p=5;//ok
但是
int *p;//只是一个地址变量,并不知道所指的空间
*p=5;//error
除了这些之外,你还知道关于它的什么吗?可能你还知道要释放.
那不释放会出现什么呢?
在<<c++程序设计教程>>(钱能著)的第14章有段很重要的话:
"c++程序的内存格局通常有4个区,
- 全局代码区
- 代码区
- 栈区
- 堆区
全局变量,静态数据,常量存放在全局数据区,所有类成员函数和非成员函数代码存放在代码区,为运行函数而分配的局部变量,函数参数,返回数据,返回地址,等存放在栈区,余下的空间都被作为堆区."
也就是说我们用new创建的空间是分配在堆区了,那堆区有什么性质呢?
"操作堆内存时,如果分配了内存,就有责任回收它,否则运行的程序会造成内存泄漏.但是在栈区,只有局部对象退出了作用域,系统自动回收.注意,对于类而言,堆对象的作用域是整个程序生命期,除非调用了delete,否则就不调用其析构函数."
如此说来,如果不用delete释放内存,就会造成内存泄漏,问题可就大了,但是有的时候并不是刚用new创建就要求析构.它的好处就是和整个程序同生命周期,那这样只要你保存了内存地址,就可以随时访问了.
最后再DELETE也不迟.
下面举出一个在MFC中非常经典的例子:
创建模态窗口与非模态是不同的,前者通常用如下代码:
Cmydlg dlg;
dlg.Domodal();
有人担心dlg是局部对象会被析构,并非如此,在执行到domodal时,程序会暂停执行,等待执行结果,传值给domodal,不过这里不是我要讲的主题.
再看看非模态窗口的创建过程:
(1)Cmydlg dlg;
dlg.Create(..);
dlg.ShowWindow();
这样的创建程序是会出错的,因为dlg获得是栈上内存,会自动析构,那关联的资源就没了.
(2)Cmydlg *dlg=new Cmydlg;
dlg->Create(..);
dlg->ShowWindow();
这种就是对的了.因为此时分配的是堆上内存,生命期为整个程序,所以不用担心内存被回收,也就不用担心窗口会显示不出了.
当然如果关闭了对话框那么这段内存就要回收了,可以用下面的代码:
void CMydlg::PostNcDestroy()
{
delete this;
}
意思就是关闭对话框后回收内存.
---------待续
评论