前言

C语言的字符串是通过数组实现的,数组本质上又和指针无二。
那么cpp的字符串底层是否跟C语言一致


正文

1
2
char ch[] = "hello world";
string str = "hello world";

在C语言里,ch现在就= ch[0]; 象征着数组首地址。
但是string的构造我们还不清楚,所以通过编译器查看

1
2
3
4
5
6
7
8
9
10
#include<iostream>
using std::string;

int main(){
string str = "hello world";

std::cout << &str << "\n" << &str[0] << std::endl;

return 0;
}

原本想通过这样直接输出的,但是cout好像优化掉了&str[0],得到的还是整个字符串的值。
那就粗暴点用C语言的printf指定格式输出

1
2
printf("%p\n", &str);
printf("%p", &str[0]);

结果是地址不同:

其实使用string的时候需要引入头文件就该知道string不简单,它也属于容器的一种,底层是通过类实现的。

再往后查看str[1]的地址:
发现str[0]和[1]直接相差1个字节,这才符合数组是由一个个char组成的。


其次我们知道C语言的字符串长度是固定的,只有动态数组才能扩容,或者说合并数组时候需要用到一块新的内存空间,string虽然有重载的办法,但是底层也是八九不离十

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
using std::string;

int main(){
string str = "hello world";

printf("%p\n", &str);
printf("%p\n", &str[0]);
printf("%p\n", &str[1]);

str += "! buhaolebuhaole";

printf("%p\n", &str);
printf("%p\n", &str[0]);
printf("%p\n", &str[1]);

return 0;
}

给str扩容,看看地址会不会发生改变

发现只有这个所谓的str首地址没有改变,剩下的0和1都变了,且幅度有点大,有点跟我们之前比较栈区和堆区之间地址的差异。


如果就只想看到字符串那一块的内存,可以使用c++11标准提供的两个函数

  • .c_str() 得到一个const char*的指针,指向字符串的内存空间
  • .data() 得到一个const char*的指针,指向字符串的内存空间

这么一看这俩方法好像是重复的,没有啥特别意义,所以在c++17标准之后统一了,只不过返回的只是个指针而不是常量指针了。

c++ 17标准

  • .data() 得到的是个char*的指针了

当我们定义一个常量指针接受str.c_str()时:

能看到cs指向的地址和str[0]是一致的。

虽然.c_str()返回的是常量指针,但是如果我们想修改,还是可以通过强制转换实现,但是这种限定是很有必要的。个人没必要闲的蛋疼去玩。

且早期的之后string不要求数组以0结尾,因为string内置累加器,会自动记录长度,通过调用length方法获取。不像C语言的时候要区分strlen和sizeof的区别。但c++11之后还是要求了string也要以0结尾


结语

主要就是区分string底层和char[]存在一定区别