【C++】内存中的字符串

开发 后端
前文 内存中的字符串类型 学习研究了Go的字符串在内存中的结构和数据类型。文本是两年多前的一篇学习笔记,研究的是C++字符串在内存中的结构。

[[422277]]

前文 内存中的字符串类型 学习研究了Go的字符串在内存中的结构和数据类型。

文本是两年多前的一篇学习笔记,研究的是C++字符串在内存中的结构。

环境

1. 操作系统:Ubuntu 16.04。 
2. 调试软件:GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1。 
3. 编译工具:g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609。 
  • 1.
  • 2.
  • 3.

string类的定义

string定义在/usr/include/c++/5/bits/stringfwd.h头文件中,如下:

typedef basic_string<char> string; 
  • 1.

basic_string类的定义通过泛型编程技术实现,详细定义请参考/usr/include/c++/5/bits/basic_string.h头文件,看起来非常复杂,具体实现此处并不关心,不再讨论。

测试string对象占用内存空间

通过以下代码可以测试string类对象占用内存空间情况。

// demo.cpp 
#include <string> 
#include <iostream> 
 
int main(int argc, char const *argv[]) 

    using namespace std; 
 
    string s15(15, 'a'); // 字符串长度15 
    string s16(16, 'x'); // 字符串长度16 
     
    cout << "sizeof(string) = " << sizeof(string) << endl; 
    cout << "sizeof(s15) = " << sizeof(s15) << endl; 
    cout << "sizeof(s16) = " << sizeof(s16) << endl; 
 
    return 0; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

因为32位和64位可执行程序不同,以下将分别编译测试。

将以上代码编译成32位可执行程序并执行,结果如下:

$ g++ -m32 -g demo.cpp 
$ ./a.out  
sizeof(string) = 24 
sizeof(s15) = 24 
sizeof(s16) = 24 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

从以上输出结果,可以十分确定string类对象在内存中占用24个字节。

将以上代码编译成64位可执行程序并执行,结果如下:

$ g++ -m64 -g demo.cpp 
$ ./a.out 
sizeof(string) = 32 
sizeof(s15) = 32 
sizeof(s16) = 32 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

从以上输出结果,可以十分确定string类对象在内存中占用32个字节。

32位可执行程序string对象的内存分配

为了查看内存分配,需要用到动态调试工具,此处使用gdb,并在源码16行设置断点。

调试过程中,打印main方法的栈数据,以及string对象及相关数据的内存,可以清晰看到string对象数据的内存占用情况。

$ gdb a.out  
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 
Reading symbols from a.out...done. 
(gdb) b 16 
Breakpoint 1 at 0x8048a80: file demo.cpp, line 16. 
(gdb) r 
Starting program: a.out  
sizeof(string) = 24 
sizeof(s15) = 24 
sizeof(s16) = 24 
 
Breakpoint 1, main (argc=1, argv=0xffffced4) at demo.cpp:16 
16      return 0; 
(gdb) x /24wx $esp  // 以16进制格式打印24个宽度为4字节的main函数堆栈数据(共96个字节) 
0xffffcdd0:  0x08048790  0x0804a0ed  0x0804a04c  0xffffced4 
0xffffcde0:  0xffffffff  0x00004a00  0xffffce08  0xffffcdf4 
0xffffcdf0:  0x0000000f  0x61616161  0x61616161  0x61616161 
0xffffce00:  0x00616161  0x0804fa10  0x00000010  0x00000010 
0xffffce10:  0x00000001  0xffffced4  0xffffcedc  0xd0415500 
0xffffce20:  0xffffce40  0x00000000  0x00000000  0xf7c86637 
(gdb) x /wx &s15    // 打印变量s15的内存地址 
0xffffcdec:  0xffffcdf4 
(gdb) x /6xw 0xffffcdec // 打印string对象s15占用的24个字节内存数据 
0xffffcdec:  0xffffcdf4  0x0000000f  0x61616161  0x61616161 
0xffffcdfc:  0x61616161  0x00616161 
(gdb) x /s 0xffffcdf4   // string对象s15的1-4个字节是一个指向字符数据的指针 
0xffffcdf4:  'a' <repeats 15 times> 
(gdb) x /16x 0xffffcdf4 // string对象s15的9-24个字节是代表数据的字符数组 
0xffffcdf4:  0x61  0x61  0x61  0x61  0x61  0x61  0x61  0x61 
0xffffcdfc:  0x61  0x61  0x61  0x61  0x61  0x61  0x61  0x00 
(gdb) x /wx &s16    // 打印变量s16的内存地址 
0xffffce04:  0x0804fa10 
(gdb) x /6xw 0xffffce04 // 打印string对象s16占用的24个字节内存数据 
0xffffce04:  0x0804fa10  0x00000010  0x00000010  0x00000001 
0xffffce14:  0xffffced4  0xffffcedc 
(gdb) x /s 0x0804fa10  // string对象s16的1-4个字节是一个指向字符数据的指针 
0x804fa10:  'x' <repeats 16 times> 
(gdb) x /16x 0x0804fa10 
0x804fa10:  0x78  0x78  0x78  0x78  0x78  0x78  0x78  0x78 
0x804fa18:  0x78  0x78  0x78  0x78  0x78  0x78  0x78  0x78 
(gdb) c 
Continuing. 
[Inferior 1 (process 20982) exited normally] 
(gdb) q 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

从以上调试可以看出,string对象的内存结构和以下结构体的非常相似:

typedef long int u32; 
struct String 

    char *data_ptr; // 指向字符数组的指针,在32位程序占用4个字节 
    u32 length;     // 字符数组的长度,在32位程序占用4个字节 
    char data[16];  // 可以容纳15个字符的数组,占用16个字节 
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

1.string对象的1-4个字节是一个指向字符数据的指针。

2.string对象的5-8个字节是一个表示字符数据长度的整形数值。

3.string对象的9-24个字节的含义根据字符数据的长度发生变化。

  • 如果string对象包含的字符数组长度小于16,则将字符数据保存在string对象本身所占用的内存中;以上述结构体String为例,将字符数据保存在data中。
  • s15.data_ptr == &(s15.data[0]);
  • 如果string对象包含的字符数组长度大于等于16,则其字符数据位于可执行文件的数据区或分配到堆内存中,而不是栈内存中;以上述结构体String为例,无法将字符数据保存在data字段中。

64位可执行程序string对象的内存分配

64位程序与32位程序非常相似,只不过64程序中,指针对象占用8字节内存;通过动态调试,发现内存分配与以下结构体非常相似:

typedef long long int u64; 
struct String 

    char *data_ptr; // 指向字符数组的指针,在64位机器占用8个字节 
    u64 length;     // 字符数组的长度,在64位机器占用8个字节 
    char data[16];  // 可以容纳15个字符的数组,占用16个字节 
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

以上内容是两年多前的学习笔记,最近在以下环境中进行测试,得到的结论与上述内容一致。

操作系统:Ubuntu 20.04。 
调试软件:GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2。 
编译工具:g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0。 
  • 1.
  • 2.
  • 3.

本文转载自微信公众号「Golang In Memory」

 

责任编辑:姜华 来源: Golang In Memory
相关推荐

2023-12-11 15:18:03

C++字符串Unicode

2021-07-30 06:22:37

C++字符型字符串

2010-02-04 17:32:43

C++中C风格字符串

2024-02-22 09:46:04

C++字符串格式化开发

2021-09-10 08:18:31

Go语言字符串

2021-08-20 06:58:31

C++Python函数

2010-02-02 11:27:16

C++字符串

2012-01-11 09:15:45

Objective-C

2010-02-04 17:39:48

C++字符串类型

2024-03-11 06:05:00

C++字符串

2024-04-01 08:41:39

字符串.NET

2010-02-02 18:01:47

C++字符串替换函数

2010-02-01 16:46:07

C++格式化字符串

2011-05-25 09:58:46

C#

2023-11-17 11:40:51

C++内存

2011-06-16 09:28:02

C++内存泄漏

2010-02-04 10:52:36

C++字符串分割函数

2013-05-02 11:13:05

C++遇到iOS应用开字符串处理

2023-10-25 13:27:20

C++字符串

2011-07-15 01:10:13

C++内存分配
点赞
收藏

51CTO技术栈公众号