10.3.1 std:string
1.纯C字符串
纯C语言使用char * 处理字符串,会自动在字符串最尾部,添加一个零字符‘\0’表示字符串的结束。
这是个绝妙的穿凿,不过也有坏处,比如有时字符串并不一定都是可视字符,如果不可视的内容含有‘\0’会难办;
再比如每一次取其长度,都需要从头到尾计算一次;
最后哪怕是这个‘\0’结尾也不是100%可依赖,C语言中的字符数组,就不会为我们自动添加结束符。
【小提示】:字符串VS字符数组
代码中,abc含有4个元素,最后一个元素视自动添加的 '\0'字符,而abc2含有3个元素。
这个特性当然是对的,有时候确实只是想要一个“字符的数组”,而不是要一个“字符串”。
困难在于接下来的是:程序员需要很清楚abc2和abc之间的区别,如果有一个函数:
void foo(char* p);
foo中对p的处理,往往依赖与它结束在 '\0'上的这个事实。但程序员一不小心,就会把abc2传递给它!
纯C字符串最难搞的还是内存管理,常见的如:只是想修改字符串的内容:
char * p = "abc";//好的C++编译环境下会得到编译警告
p[0] = 'A';
...
linux下的执行情况:
这样做,程序会死的......后面再谈
#include <cstring> //strcpy()
.....
//一个初始化,牵涉4个知识点
//1)内存分配
//2)字节尺寸
//3)类型强转
//4)结束字符
char * p = (char*)malloc(9 * sizeof(char));
//strcpy不安全,复杂情况下建议使用strncpy(...)
//前面的‘9’ = "d2school"的长度 + 1
strcpy(p, "d2school");
//往短处改...
//方便,不过占用内存还是9 byte
strcpy(p, "school");
//实际内存不增加,但增加有效内容的字符个数
//立即数很讨厌
p[6] = '-';
//别忘了自己补零
p[7] = '\0';
//往长处改...
//realloc保障原内容还在
p = (char*)realloc(p, 10 * sizeof(char));
strcat(p, "D2");
...
//释放:
free(p);
在C++环境下,以上代码也可以使用new[ ]/delete[ ]处理,但在模拟realloc(保留原有内容时),只会更复杂。
所以,如果程序中有大量字符处理,特别是修改字符串内容的操作,请自行或从别人那里搞一套字符串管理工具,通常都给予复杂的后果。
纯C字符串还有一些小特性是初学者的陷阱,比如用户会直觉地使用“==”来判断两个字符串是否相等,但这样其实是在比较两个字符指针的指向是否相等:
char const * p1 = "ABC";
char const * p2 = "ABC";
if(p1 == p2)
{
...
}
除非代码太优化而带来副作用,否则以上条件判断理所当然不成立,因为p1和p2各自指向一旦内存(尽管内存中的内容刚好一样)。
编译器还是太优化了!
再如,由于需要兼容很长一段历史上C语言没有const修饰的问题,编译器允许用普通的char*指向一段其实是不可修改的内存:
//其实是:char const* ppp = "Tom";
char* ppp = "Tom";
ppp[1] = 'i';
cout << ppp << endl;
/*上述代码想修改ppp的内容,所以正确写法
是用字符数组(或者在堆中分配)*/
char ppp2[] = "Tom";
ppp2[1] = 'i';
cout << ppp2 << endl;
执行以上代码,通常程序会挂掉,除了const问题之外,还需要理解代码中,ppp是指向一段静态数据段,而ppp2则是在栈中分配内存,再从静态数据段中复制字符串内容……