Solmyr 的小品文系列之一:字符串放在哪里? Elminster -------------------------------------------------------------------------------- 画外音:

Solmyr   的小品文系列之一:字符串放在哪里?  
Elminster  

--------------------------------------------------------------------------------

画外音:今天是个大晴天,温暖的阳光透过窗子照进了这间宽敞的办公室,办公室里三三两两的人们正在各自的计算机前努力工作,一切都显得那么的安静、祥和、有条不紊   ……  

“啊~!救命啊!Solmyr   你又用文件夹砸我!”  

“愚蠢者是应该受到惩罚的。”  

画外音:   ……   呃,好吧,我得承认有点小小的例外。这里是一家软件公司,发出惨叫的这位是   zero   ,新进的大学生;这边一脸优雅,看上去很有修养一点也不象刚刚砸过人的这位,是   Solmyr   ,资深程序员,负责   zero   这一批新人的培训。啊,故事开始了   ……  

“我干了什么啦?”zero   揉着鼻子问道,“这次你拿来砸我的文件夹又大了一号!”  

“你过来自己看看你犯下的错误。”Solmyr   翻出了   zero   刚刚交上来的一段代码:  

……  
char*   msg   =   “Connectting   ...   Please   wait“  
……  
if(   Status   ==   S_CONNECTED   )  
strcpy(msg,   “Connectted“);  
……  

“我犯了什么错误啦?这是一个很平凡的字符串声明而已”,zero   不满的说到。  

“你看不出来吗?connect   这个单词的进行时和过去时你都拼错了,多打了一个   t”,Solmyr   不紧不慢地回答。  

“就为了这个你又用文件夹砸我   ……   啊!这次又是光盘盒!”  

“这是商用软件,你以为是在   QQ   上和   PPMM   聊天,有错别字不要紧啊?更糟糕的是,我故意留了这么长的时间给你,到现在你还没发现你真正的错误在什么地方。你可真不是一般的菜啊~”,Solmyr   故意拖了个长音,满意的看到   zero   处于爆发的边缘,“好吧,让我们从基础开始,C   语言中是怎样处理字符串的?”  

“这个我知道”,zero   显得很有自信,“C/C++   语言中,字符串是一段连续的字符型内存单元,每个单元存放一个字符,并用   \0   作为结尾的标记。”  

“那么使用指针之前,我们应当   ……”  

“我们应当保证这个指针指向合法的内存,要么指向一块已经存在的内存,要么为它动态分配一块。”,zero   开始露出得意的笑容   ——   这种程度的问题,哈!  

“好!那么你的代码中   msg   这个指针指向哪里?”  

笑容凝固了。  

“这个   ……   呃   ……   我想   ……   它应该指向一块合法内存,因为以前我这么做的时候,它能工作   ……”,zero   期期艾艾的说。  

“合法内存?这块内存是谁分配的?它有多大?生存周期多长?有哪些特殊的性质?”  

“……”  

“唉!”,Solmyr   重重的叹了口气,“我就知道会这样。好吧,让我们先从简单的开始。”。Solmyr   飞快的键入了如下代码:  

char   msg[]   =   “Hello“;  

char*   pmsg   =   (char*)malloc(   sizeof(“Hello“)   );  
strcpy(pmsg,   “Hello“);  

“上面这些代码你应该都很清楚了:msg   是一个字符数组,C   语言保证会为它分配一段连续的内存,并将其初始化为   “Hello“   。pmsg   是一个字符指针,我们调用了   malloc   函数为它动态分配了一块内存,并用   strcpy   函数填充其值为   “Hello“   。这两种做法的共通点是:首先用正常手段获得一段内存,然后填充值。接着再来看这个:”  

char*   msg   =   “Hello“;  

“这一句代表什么意思?首先   msg   是个指针,C/C++   语言不负责为它分配一块内存;其次我们也没有显式的为它分配一块内存。它指向哪里?指向   “Hello“   ,就是你直接写在代码里的那一个。”  

“什么叫做‘直接写在代码里的那一个’?”,zero   露出了困惑的表情  

“举个例子你就明白了:”,Solmyr   再键入:  

double   db   =   1.5;  

“这一行里面,1.5   是个什么东西?它是一个   double   类型常量,C/C++   语言要处理它们,也要分配内存来存放这些东西。同理,当你在代码里写了   “Hello“   ,实际上   C/C++   语言就分配了一块内存存放这个字符串,当你写   char*   msg   =   “Hello“   的时候,你就是把这样一块内存的地址赋给了指针   msg   。所以   msg   确实指向一块合法内存,这是有时候这段代码能够工作的原因。但是这样做,其中蕴涵了许多问题,我来问你,指向这块内存的指针应该是什么类型?”  

“当然是   char*”,zero   不加思索的回答。  

“错!应该是   const   char*   。想当然耳,写在程序中的字符串你不希望它发生变化,所以很明显的,这块内存应该被解释为常量。但是你在声明   msg   的时候做了什么?”  

“呃   ……   我用了一个非常量的指针去指向了一个常量字符串。”,这一次,zero   明显的审慎多了。  

“正确。看你原来的代码,你不仅用一个非常量指针指向它,而且还对这个指针执行了   strcpy   ,往里写了内容。在我们的编译器上,这么做会引发什么后果?”  

“呃   ……   引发一个运行时错误?”  

“部分正确。准确的讲,只有在工程编译选项为调试版本的时候,如果工程编译选项为发布版本,一切都很正常   ——   奇怪吗?并不,记住这一点:C/C++   允许你打破任何保护。所以如果这两行代码在调试的时候没有被发现而溜进了发布版本里”,说到这,Solmyr   狠狠的瞪了   zero   一眼,“将会是很难发现的。”  

“可是说来说去这么做还是没有什么危害不是吗?msg   指向一块合法内存,内容正确,而且也并不是真的不能写入,有什么好担心的呢?”,zero   抱怨道。  

Solmyr   顺手抓起杯子,zero   反射性的立刻缩头护脸。“别担心,我只是喝水而已。”,Solmyr   面无表情   ——   如果忽略他嘴角那一丝坏笑的话   ——   的说到,“没有危害是吗?看看下面的代码:”  

char*   str1   =   “Hello“;  
char*   str2   =   “Hello“;  
*str1   =   ‘P‘;  
cout   < <   str2   < <   endl;  

“猜猜运行结果是什么?”,Solmyr   一边调整工程设置,一边问道。  

“这还用问吗?当然是输出   Hello   了。”  

“回答错误,正确答案是   ……”,Solmyr   按下了运行按钮,屏幕显示的居然是   Pello   !。  

zero   大为诧异,挠着头试图找出其中的逻辑,突然间灵光一闪:“我明白了!str1   和   str2   实际指向同一段内存!因为   C/C++   语言在处理   Hello   字符串的时候把它当作常量,所以就做了优化,只保存了一份   Hello   !是不是这样!”zero   兴奋的转向   Solmyr。  

“嗯,看起来有时候你也不是那么菜么”,Solmyr   赞许的点头,“不过你还是说错了一点:这个不是   C/C++   语言的做法,是这个编译器的做法。简单的说,你如果要对这种字符串写的话,其结果如何,是没有定义的。所谓没有定义,就是   C/C++   语言不保证会得到怎样的结果,可能这样也可能样,完全决定于你的编译器作者怎么想。想想看吧,哪天你的程序出现了古怪的问题   ——   比如显示信息出现了混乱   ——   起因却是你在无关的地方写了一个字符串,会怎样?这是维护时最大的恶梦之一。现在你明白危害在哪里了?”  

zero   有如大梦初醒一般忙不迭地点头:“我知道了,我知道了。”  

“知道了还不快去改!”  

……  

zero   跑回坐位修改他的程序去了,办公室里再度恢复了宁静,所有的人都埋头于他们的工作之中。只有   Solmyr   一边喝着咖啡一边揉着太阳穴,喃喃地吐出不祥的词句:“这样的日子才刚刚开始啊   ……”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值