C语言中的匕首
- C风格字符串
不同于其他主流编程语言,C语言本身对于字符串的处理采用了独特的设计,被称为C风格的字符串,以区别于C 创立的字符串string。
设计
C风格字符串由一串字符在内存里顺序存放,在最后一个字符串后追加一个终止字符。这充分条件体现了C语言哲学 - 简单的事情简单化。
例如字符串'Hello world!',用C语言定义是这个样子的。
char str2[] = 'Hello world!'; /* str2字符串的长度可以省略,由编译器自行处理 */
C语言中的字符串本质上是字符数组,它的特别之处在于,在末尾字符'!'后,必须追加一个终止字符'\0',才能成为一个有效的C-风格字符串。记住这点!
在常见的计算机中,C语言的字符类型占用1个字节。不难看出,C风格字符串在计算机里的存储空间总是字符串长度加1。例如'hello'字符串所需要的存储空间是6个字节。对比之下,C 的string类将占用24个字节。
示例代码
#include <iostream>#include <cstdio>#include <string>int main(){char sp1[] = 'hello';std::string sp2 {'hello'};printf('c-style string: %s. size = %zu\n', sp1, sizeof(sp1));std::cout << 'c string: ' << sp2 << '. size = ' << sizeof(sp2) << '\n';return 0;}
运行结果为:
c-style string: hello. size = 6c string: hello. size = 24
当然,我们不能据此认为C 的std::string不好!std::string作为类,具有类的种种好处,同时支持unicode,还内置了很多成员函数。
C风格字符串的优势在于紧凑存储空间,高效存取,与C语言指针高度融合。运用之妙,存乎于心。我们要善于利用C风格字符串的优势,选择性地在C和C 中使用它。C风格字符串就好比匕首,便携方便,握在行家手里,是致命的武器。
存取
如何存取C风格字符串呢?由于C风格字符串在内存中是顺序存放,那么用指针和指针偏移量就能高效存取字符串内容。请看:
char str1[4] = '123';assert(str1[0] == '1');assert(str1[1] == '2');assert(str1[2] == '3');assert(str1[3] == '\0');str1[3] = '4'; printf('%s', str1); /* 打印字符串,值为124 */
字符串str1的指针名就是str1。这是C语言设计者给予我们的礼物。str1[1]的意思是调取str1指针偏移1指向处,这相当于*(str1 1)。
常见问题
C语言初学者往往难以驾驭C风格字符串。究其原因无外乎两点,
1)忘记为字符串的末尾的终止符分配内存;
char str1[3] = '123'; /* 错误。忘记为终止字符分配空间 */
2)对字符串操作时忘记处理末尾的终止字符。
例如,拷贝一个字符串的前两个字符到新字符串。示例代码如下
char source[64] = 'hello world';char dest[64];int copy_chars = 2;strncpy(dest, source, copy_chars); dest[copy_chars] = '\0'; /* 必须的一步! */
假如少了最后一步,那么dest不是预期的C风格代码!后果非常严重。
“C语言程序员知道自己在干什么” --C语言之父丹尼斯瑞奇。
其实,只要牢记两点,就能使用C风格字符串这把匕首。
1)总是为末尾终止符分配内存空间
2)处理C风格字符串时,记得处理好末尾的终止字符