![C++新经典](https://wfqqreader-1252317822.image.myqcloud.com/cover/184/44510184/b_44510184.jpg)
6.3.3 字符串和字符串结束标记
这一小节内容是重点中的重点,请务必好好学。
刚才举了如下这个例子,先回顾一下:如果提供的初值个数和预定的数组长度相同,定义时可以省略数组长度,系统会自动根据初值个数确定数组长度:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p107_85613.jpg?sign=1739284874-YDA3rsfp5Wt2qbRB5TBeEuURmX8nUXb8-0-3aa7d0c2d8cab1200c22dbfb6cbe0ee5)
跟踪一下不难发现,上面这行代码其实等于定义了一个包含10个元素的数组(char c[10];),能够引用的元素是c[0]~c[9]。
现在,要补充一个对字符数组初始化的方法,也就是用字符串常量来初始化字符数组。看看下面这行代码:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p107_85614.jpg?sign=1739284874-rf85jwucOS0CfZbtASKD4EZhSxiYnLSp-0-7e25e47887bfc1e435167b29276b8c39)
如果设置断点跟踪调试,会赫然发现,上面这种初始化居然是定义了一个形如“char c[11];”的数组,也就是该数组的长度是11,意味着能够引用的元素是c[0]~c[10],并且c[10]里面被系统自动填充进去一个'\0'字符。前面讲过,在计算机内存中保存的是字符的ASCII码,查询一下ASCII码表就可以知道'\0'的ASCII码是0,所以'\0'其实就是0。跟踪截图如图6.10所示。
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-P108_47754.jpg?sign=1739284874-gNzmFZYJnrr3Nvue8sj4Iy4OdY9jiwCo-0-f314f2b6e6d807f7181a812babc10c41)
图6.10 代码“charc[]={"Iam happy"};”跟踪截图
现在正式介绍这个'\0'。'\0'称为字符串结束标志。这个字符串结束标记,用来标记一个字符串的结束。为了测定字符串的实际长度,C语言规定了一个字符串结束标记,用'\0'代表,如果一个字符串的第10个字符为'\0',则该字符串中的有效字符为9个。也就是说,在遇到字符'\0'时,代表字符串结束,由'\0'前面的字符构成整个字符串。
第2章中看到过字符串常量,例如"I am happy"就是字符串常量。实际上,C语言对字符串常量也会自动在其末尾增加一个'\0'作为字符串结束标记,例如"I am happy"一共有10个可见字符(空格也算可见字符),每个可见字符占1字节内存,但实际上该字符串在内存中是占11字节,最后1字节存放的正是'\0'。
看如下代码,这段代码涉及字符指针p,该字符指针在这里用于指向字符串"Iam happy"所在的一段内存,指针的概念后面章节会详细讲解:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p108_85617.jpg?sign=1739284874-N1Be0gpToYnYdPBW3deXdJxUIf284nfg-0-f4f93db88e50726f01833b1ab2e626a6)
现在主要目的是观察一下"Iam happy"这个字符串在内存中究竟是什么样子,可以利用第2章学习过的知识来给代码设置断点,并观察内存,如图6.11所示。
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-P108_47776.jpg?sign=1739284874-tIWfdqVtiSgLstgbQIL8L2TkUa8kJZmz-0-db82540ee5f48df56de268589ae01cb8)
图6.11 字符串"Iam happy"在内存中存储示意图
在图6.11中不难发现,在"Iam happy"字符串的最后一个可见字符'y'的后面有一个00,这其实就是一个数字0,也就是字符串结束标记'\0',是由系统自动加上去的。
有了字符串结束标记'\0'之后,字符数组的长度就很容易确定了,在程序中往往依靠检测'\0'来判断字符串(也就是字符数组中保存的内容)是否结束,当然,在定义字符数组时,必须要估计实际要保存的字符串长度,定义字符数组时指定的长度必须要不小于字符串的长度。如果在一个字符数组中先后存放多个不同长度的字符串,则定义字符数组时的长度应该不小于最长的字符串长度。看如下代码:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85621.jpg?sign=1739284874-8EWHwwiN2nff8p2uRVaOlUL6E8MM5es9-0-9e44b48ca81ef3c410630cd4dc47e9df)
上面的代码是错误的,为什么?因为数组c大小是10,无法保存下"Iam happy"这个字符串,刚刚说过,C语言对字符串常量会自动在字符串末尾加一个'\0'作为字符串结束标记(该标记也要占一个位置),而上述初始化数组c的方式显然也会把字符串常量里面的\'0'放入字符数组c中去,所以在数组c中必须要为'\0'字符留有位置,因此,必须至少要把字符数组c的大小设置为11。看如下代码:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85622.jpg?sign=1739284874-TB7yU1Fofx7Bm5i4ChuQ5Uhok4ckFtbp-0-591fc335ee273e03ae78b1a4704ea7e9)
所以,如下范例:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85623.jpg?sign=1739284874-zIo2awbx8PW9QxRyexywmh4ftZXgZaPs-0-7ef1e4ec70e42f620dfabc9fe893a92f)
所以请想一想下面两行代码的区别:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85624.jpg?sign=1739284874-9Hvf0SvF93alFEFaRWGpnvjKzwvLnlkx-0-f60f0a33330b61fe3cbc1c9d978ca7d2)
上面两行代码是不等价的,因为后一种写法系统会自动在字符串末尾增加个'\0'。但是,下面这种写法:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85625.jpg?sign=1739284874-sa26ehgww5fMRgml6HSMDxfOUhbjzTET-0-2f7c669b85c92897381775fb7cd3024c)
就会等价于:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85626.jpg?sign=1739284874-iT7hGsBRRoGLm16EWL6CX6ho2OHlVMF3-0-7c82e62577582854ab17ff7308e5d9cb)
也就是说,它们的长度相等,内容也相同。
再次强调,对于字符串(双引号包含起来),系统会自动往末尾增加一个'\0',当然,自己手工往末尾增加一个'\0'也是可以的。
这里还有一点要强调,字符数组并不要求它最后一个字符为'\0',甚至整个字符数组可以没有'\0'。例如:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85628.jpg?sign=1739284874-MAa4icbhBU3RYZqvUPcuCjmCUJuwqGIB-0-9c39838403c028ec3ad21b068dd1f710)
是否加个'\0'取决于需要,但是,只要用字符串来初始化字符数组,系统就会自动在字符串末尾加一个'\0'。例如如下代码:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p109_85629.jpg?sign=1739284874-WqeooLVuVMHctq6H8DuXzc0Gfq2dew1a-0-f6c026fff28115429e846b77a1894538)
所以如果要使用字符数组并对其进行初始化,建议和字符串保持一致,也就是人为地增加一个'\0',这样做主要是为了确定字符串的实际长度(因为字符串实际长度是靠找到末尾的'\0'来确定的)。
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p110_85630.jpg?sign=1739284874-8wr6SfAMjhzveFGlFyCAAZvIAmoe4Biy-0-ffa90d72d1ebd1046e8f6138a2aa13b7)
不过上面这种写法很罕见,一般都不会给字符数组中每个元素分别赋值,而是下面这种写法。笔者建议,重点掌握下面这种写法即可:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p110_85631.jpg?sign=1739284874-7NGhxBdvgM2Jw9PSgRPIaZFyNNFS5pJE-0-acd4ec289fb4915dacfe5a574673239a)
为了加深对字符串结束标记的记忆,现在再仔细地看一个范例。如下代码:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p110_85632.jpg?sign=1739284874-Ca721Ji9ROfJnjgWnOmkDYb3ReIMV6lr-0-45bc4c74b8e4943da602613abe90b41d)
现在执行这段代码,结果如图6.12所示,可以看到出现了垃圾信息。
但是,下面这段代码:
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-p110_85633.jpg?sign=1739284874-fmS0TKN1PWKMor0aSnPdkF36SMjeAiAF-0-73404547a7e2bba8072a3ee3e0c62af6)
执行这段代码,结果如图6.13所示。
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-P110_47879.jpg?sign=1739284874-tKGgVuxhcD1VBSQqsZNrWbofHpCQOTBr-0-88c41550b4256d19133b6fbfb3f0ac11)
图6.12 没有字符串结束标记导致输出时出现了垃圾信息
![](https://epubservercos.yuewen.com/F329F6/23721607801994206/epubprivate/OEBPS/Images/Figure-P110_47883.jpg?sign=1739284874-Qptq9E7P1dZmbx3WdjhWdZvMr7Lxoldj-0-8e35ea9d928500d4da36d838f20b6c97)
图6.13 含有字符串结束标记,输出的字符串结果很正确
这里看到了正常的输出,没有任何多余的垃圾信息,这是因为,在图6.12中,向屏幕输出字符串内容,一直遇到'\0'才会停止输出,而因为内存中一直没有遇到'\0',就会一直向屏幕输出内容,所以看到了很多垃圾信息,终于,偶然之间(无意中)遇到了一个'\0',于是停止了输出,这种写法的程序代码肯定是有问题的。但反观图6.13,因为系统自动给字符串末尾增加了一个'\0',所以向屏幕输出字符串内容时,遇到'\0'刚好停止输出,所以结果非常正确。
本节内容即将结束,读者的主要任务是理解好字符串结束标记,一定不能忘记,面试的时候,这很可能是考点,若因回答不好而丢掉工作机会将非常可惜。