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 一边喝着咖啡一边揉着太阳穴,喃喃地吐出不祥的词句:“这样的日子才刚刚开始啊 ……”

技术小品文(一)字符串放在哪里?相关推荐

  1. java字符串元素置于最前_java_java编程常用技术(推荐),一:将String字符串放在最前面 - phpStudy...

    java编程常用技术(推荐) 一:将String字符串放在最前面 防止发生NullPointerException异常,我们通常把String字符串放在equals方法的左边来比较,这样可以有效的避免 ...

  2. Go语言逆向技术:常量字符串

    摘要:Go语言源代码编译成二进制文件后,源代码中的字符串存放在哪里?是如何组织的? 本文分享自华为云社区<go语言逆向技术之---常量字符串解密>,作者:安全技术猿. Go语言源代码编译成 ...

  3. [转载] python程序所需的图片通过base64编码成字符串放在代码中

    参考链接: 在Python中编码和解码Base64字符串 问题背景 python代码打包成exe可执行文件.问题是这个python程序执行时需要显示一张图片,这就意味着打包成exe后,也需要在exe同 ...

  4. go语言逆向技术之---常量字符串解密

    [摘要]go语言编译出来的二进制文件中,字符串数据是如何存放的,逆向时如何快速和准确的识别出源代码中定义的字符串,本文给你解密. **Go语言源代码编译成二进制文件后,源代码中的字符串存放在哪里?是如 ...

  5. 计算机硬件技术基础 统计字符串strl中字符'a'的个数,汕头大学工学院830计算机基础综合历年考研真题202p.doc...

    目 录 第一部分 汕头大学工学院830计算机基础综合历年考研真题汇编5 2014年汕头大学工学院830计算机基础综合考研真题5 2013年汕头大学工学院830计算机基础综合考研真题11 第二部分 全国 ...

  6. for死循环、怪异字符串、两次return……Python冷知识(三)

    本文转载自Python编程时光(ID:Python-Time) 冷知识系列,已经更新至第三篇.前两篇传送门小明给你准备好了,还没阅读的可以学习一下. 谈谈 Python 那些不为人知的冷知识(一) 谈 ...

  7. 通过解决“构造包含所有给定子串的最短字符串”问题思考算法优化

    最近由于工作相对比较忙,需要学习一些新的技术项目,写代码的时间比较少.继续解决百度2017秋招4星的题目,今天要分析的这个题目,是目前我遇到相对其他4星题目算是有一点难度的题目. 今天我们将从一个题目 ...

  8. 10个实用的但偏执的Java编程技术

    在沉浸于编码一段时间以后(比如说我已经投入近20年左右的时间在程序上了),你会渐渐对这些东西习以为常.因为,你知道的-- 任何事情有可能出错,没错,的确如此. 这就是为什么我们要采用"防御性 ...

  9. lua 字符串包含_Programming in Lualua学习第11期 Lua模块与包

    微信公众号:GameToolDev 关注可了解更多的游戏工具开发教程.问题或建议,请公众号留言; 从Lua 5.1开始,我们可以使用require和module函数来获取和创建Lua中的模块.从使用者 ...

最新文章

  1. oracle表名最大长度6,Oracle中表名的最大长度是多less?
  2. scrapy爬虫系列之五--CrawlSpider的使用
  3. KNIME二次开发的环境配置安装过程
  4. redis集群添加master节点
  5. 基于centos6.5搭建solr服务器
  6. XSSFWorkbook 设置单元格样式_如何设置Excel单元格才能只输入数字!
  7. Python可视化工具Matplotlib 3.0版出炉,改进默认后端选择,饼图终于变圆了
  8. 一个长方体玻璃容器从里面量长宽_葡萄干这样吃,功效翻倍,含铁量是葡萄的15倍!葡萄干的功效和作用...
  9. STM32+uCOS-II+uc/GUI移植 (uC/GUI API函数学习一)
  10. logback.xml日志配置文件,springboot
  11. WebX框架解析及使用教程
  12. 管家婆的验证服务器失败,管家婆登陆提示“连接服务器失败”怎么办
  13. 如何查看路由器的MAC地址
  14. Dijkstra算法(迪杰斯特拉算法)
  15. 单点登录SSO:可一键运行的完整代码
  16. redis 客户端 -- lettuce 介绍
  17. USB转串口电路之CH340G
  18. 计算机网络ip地址划分方法,ip地址怎么划分 ip地址划分方法【图文】
  19. [BZOJ4784][UOJ290][ZJOI017]仙人掌-树形DP
  20. python中lcut什么意思_python中如何画火山图

热门文章

  1. 百度SEO逆推技术软件下载,引蜘蛛秒收录
  2. realmeq参数配置详情_realmeq手机怎么样?参数配置详情
  3. MacBookPro 安装cx_Oracle,并配置环境
  4. 关注幼儿教育-儿童木工DIY室、木工坊
  5. centos7磁盘挂载及目录扩容
  6. 上海计算机二级python_上海市高等学校计算机等级考试(二级)《Python程序设计
  7. 修复之前写的模拟I2C程序,增加多总线,时序更精确操作
  8. 什么是服务器托管(服务器托管方式的特点)
  9. http://xing8s8.com/index.php,robot framework
  10. rabitMQ work模式二 按能力分配任务