1 char * 和 std::string 的区别
char * 字符串是常量字符串,不能修改;std::string 指向的字符串可以修改
实例代码如下图所示,s1 和 s2 均是常量字符串,字符串常量保存在只读数据区,是只读的,不能写,代码中注释的那两行代码会导致段错误。
s3 是字符数组,字符数组是可以修改的,std::string 类型的字符串也是可以修改的。
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <cstring>
int main() {
char *s1 = "hello";
char *s2 = "hello";
char s3[] = "hello";
std::string s4 = "hello";
printf("s1 addr = %p, s2 addr = %p, s3 addr = %p, s4 addr = %p\n", s1, s2, s3, s4.c_str());
printf("sizeof(s1) = %d, sizeof(s3) = %d, sizeof(s4.c_str()) = %d\n", sizeof(s1), sizeof(s3), sizeof(s4.c_str()));
// s1[1] = 'a';
// s2[1] = 'a';
s3[1] = 'a';
s4[1] = 'a';
std::cout << "s1 = " << s1 << ", s2 = " << s2 << ", s3 = " << s3 << ", s4 = " << s4 << std::endl;
return 0;
}
代码运行之后,可以看到 s1 和 s2 是常量字符串,这两个指针指向的地址也是相同的。
从 s1,s2 的地址和 s3,s4 的地址对比可以看出,s3 和 s4 的地址相距比较近,和 s1、s2 的地址相距比较远。s1、s2 和 s3、s4 保存的段都不一样,前者保存在只读数据段,后者保存在栈,所以地址差距才会大。
2 std::string 是深度拷贝
如下图所示, 有 3 个字符串,s1、s2 和 s3。
s2 是基于 s1 通过拷贝构造而来,将 s2[1] 赋值为 'a',然后打印 s1 和 s2,s1 仍为 "hello",s2 为 "hallo",说明 s1 和 s2 相互不影响,是深拷贝。
s3 被 s1 赋值,将 s3[1] 赋值为 'm',然后打印 s1 和 s3,s1 仍为 "hello",s3 为 "hmllo",s1 和 s3 相互没有影响。
#include <iostream>
#include <string>
int main() {
std::string s1 = "hello";
std::string s2 = s1;
std::cout << "1, s2 = " << s1 << std::endl;
s2[1] = 'a';
std::cout << "2, s2 = " << s2 << ", s1 = " << s1 << std::endl;
std::string s3 = "aaa";
s3 = s1;
std::cout << "1, s3 = " << s3 << std::endl;
s3[1] = 'm';
std::cout << "2, s3 = " << s3 << ", s1 = " << s1 << std::endl;
return 0;
}
运行结果如下:
3 std::string capacity()
如下代码,声明了一个空字符串 s1,打印出来了 s1.capacity() 是 15,也就是说 std::string 默认就会有 15 的空间。如果字符串的大小不大于 15,就不会申请堆空间,如下代码可以验证。
执行结果:
使用 gdb 调试,对 malloc 设置断点,可以查看上边的代码哪一行调用了 malloc()。
第 11 行:
字符串长度是 16,大于 15 了,需要申请内存。
第 12 行:
拷贝构造,深拷贝,长度大于 15,需要申请内存。
第 14 行:
赋值运算符,长度大于 15,需要申请内存。
第 16 行:
新创建的 s6,长度大于 15,需要申请空间。
代码第 17 行,将 s4 赋值给 s6,虽然 s4 的长度大于 15,但是当前 s6 的 capacity() 是 20,完全能够放得下 s4,所以不需要再申请空间。