一个字符串常量实验引发的思考

来源:互联网 发布:初级网络优化工程师 编辑:程序博客网 时间:2024/06/05 18:29

一、实验回顾

 有这么一段程序(编译环境 vs 2013),如下:

#include "stdafx.h"int _tmain(int argc, _TCHAR* argv[]){char a[] = "abc";char b[] = "abc";char* pstr1 = "abc";char* pstr2 = "abc";if (a==b){printf("true\n");}else{printf("false\n");}if (pstr1==pstr2){printf("true\n");}else{printf("false\n");}return 0;}

这段程序意图很明显,将字符串常量分别赋值给数组和指针,然后试图通过==判断是否相等(这里当然是判断地址是否相等,你不会想到判断值相等去了吧 - -!)。对于a==b,我分析对了,它们确实不相等。但是pstr1==pstr2,它们居然相等!

    我将a,b ,pstr1,pstr2的地址打印了出来,如下图所示:


很遗憾的是,它们确实相等,地址都是2119768。但是这是为什么呢?搜了一下,发现了答案,因为字符串常量分配在了静态存储区!下面是关于内存基本构成的一段解释:

可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。

静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

好,现在再回到我们的程序中来。我们将字符串常量"abc"赋值给了数组a和b,其实是将"abc"拷贝到了a,b所在内存中,很显然这里的内存是栈区。因为a,b所占内存不同,所以a==b的结果是false。当我们将字符串常量"abc"赋值给了指针pstr1和pstr2后,其实是将指针指向了"abc"所在存储位置---静态存储区(因为指针pstr1和pstr2并没有通过new/malloc分配内存)。由于赋值给pstr1和pstr2的"abc"都是同一个常量,所以尽管它们赋值给了两个不同的指针,其实都是同一个"abc"。既然pstr1和pstr2指向的都是同一个"abc",那么pstr1==pstr2为true就很好理解了!

当我们把赋值给pstr2的字串改成"abcd"时,由于"abc"和"abcd"显然是两个不同的常量,所以存储位置肯定不同,那么pstr1肯定就不等于pstr2了,如下所示:

#include "stdafx.h"int _tmain(int argc, _TCHAR* argv[]){char* pstr1 = "abc";char* pstr2 = "abcd";if (pstr1==pstr2){printf("true\n");}else{printf("false\n");}printf("%d\n", pstr1);printf("%d\n", pstr2);return 0;}
运行结果:


此外,由于指针指向的是字符串常量,所以一般用const修饰一下比较好:

      const char* pstr1 = "abc";
之所以这么做,是因为凡是指向"abc"的指针,都指向的同一块存储位置。你把"abc"赋值给数组,没关系,反正是要拷贝到各自数组中去的,所以你即使写下如下的语句也没关系(即把字串值改变):

     a[0] = 'e';
但是你通过指针改变字符串常量的值,那就很危险了,例如:

#include "stdafx.h"int _tmain(int argc, _TCHAR* argv[]){        char* pstr1 = "abc";char* pstr2 = "abc";pstr1[0] = 'e';printf("%s\n",*pstr1);printf("%s\n", *pstr2);return 0;}
这个程序运行时会报错,如图所示:



原因是"abc"是字符串常量,存放在静态存储区,不能修改。我们从逻辑上也好理解,很多指针都指向了"abc",你某个指针如果可以改动它的话,那别的指针岂不是要完蛋?

为了使程序更加健壮,所以我们给pstr1和pstr2加上const关键字:

   const char* pstr1 = "abc";      const char* pstr2 = "abc";
这样如果出现修改指针内容的语句,编译将不会通过,报错如下:


这样我们就可以及时发现bug并修正它。

参考文章:

1、静态存储区、堆和栈的区别

2、常量字符串内存分配问题

3、C语音const关键字详解

0 0
原创粉丝点击