c++(7`数据大小、内存大小和计算)
数据类型的大小
长度的单位为字节,右边是可表示的十进制数字范围
不同位系统,long和指针的字节数可能不同
内存五大分区
- 内核空间:存放操作系统内核代码、内核数据(如进程调度、内存管理……),是不可直接读写的
- 堆区:存储由程序员动态分配(new/malloc/智能指针……)的对象,存放对象的生命周期是动态的
- 由于new/delete并不会遵守堆的先进先出的特性,会造成内存碎片,使程序效率降低(寻址)
- 空间大:对于32位系统,可达2^32,但是仍要保持清理内存,否则访问越界会造成程序崩溃
- 堆的机制是很复杂的,它比栈的效率要低
- 栈区:存储局部对象,它们的生命周期是自动的
- 先进后出的结构,不会造成内存碎片
- 容量小:大约只有2MB
- 执行效率很高
- 静态区/数据段:存储全局变量,静态变量,它们的生命周期是静态的
- 常量区/代码段:存储程序指令、函数编译后的可执行的二进制代码、只读常量,它们的生命周期是也是静态的,但它们是只读的不能修改
注意: 指针变量本身是存放在栈区的,而指针所指向的对象可以位于栈区、堆区或者全局/静态存储区,它是根据对象本身决定的
size_t
size_t 是一种平台相关的无符号整数类型,足够容纳任何对象大小的无符号类型,在32位系统上,它通常是4字节的unsigned int;而在64位系统上,它通常是8字节的 unsigned long
32 位和 64 位指的是CPU(处理器)的字长,字长是CPU在一次计算中能处理的二进制数据的最大位数,它决定了数据处理速度、寻址范围(通常地址总线位数和字长一致)
1
2
3
4
5
6
7
size_t size = sizeof(double); //标准库兼容性
size_t length = vec.size();//标准库兼容性
int arr[5000000000];//对象数量
for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
}
const size_t huge_size = sizeof(……); //内存大小
展开
size_t足够容纳任何对象大小的无符号类型:
假设是32位CPU,size_t表示的范围是0——4294967295
按字编址,地址总线的位数是32,即寻址范围共2^32字节 == 4,294,967,296 字节 == 4G
也就是说一个对象最大的sizeof值不会超过4,294,967,296,size_t恰好可以存储
作用:
- 标准库兼容性:C++标准库中的许多函数和操作都会使用size_t作为返回类型,
- 用于循环计数,避免因使用 int而可能导致的溢出问题,比如 int arr[5000000000],这个在64位是可以存储下的,数组的个数是5000000000,因此int不支持,而size_t此时相当于 long,确保代码的可移植性和安全性
- 需要注意的是,它是unsigned的,因此不要和signed混用,会产生错误的结果,并且具有逻辑上的非负性,所以通常用来表示内存大小/对象数量,而非需要运算的情况
sizeof和strlen
1
2
3
int *arr = (int *)malloc(n * sizeof(int));
size_t personSize = sizeof(struct Person);
size_t arrLength = sizeof(arr) / sizeof(arr[0]);
展开
sizeof()是运算符:
- 返回一个对象/类型所占的内存大小,单位是字节,数可以是数组、指针、类型、对象、函数等,
- 其值在编译时 就已经计算好了,因此参数不可以是动态分配的对象
- 通常用于为静态类型分配足够的空间,免除了复杂的计算
- 帮我们计算复杂类型的大小
- 计算数组的元素个数
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
struct T{
char a; //偏移量0,
short b; //偏移量 == 上一个偏移量 0 + 上一个成员的大小 1,调整为2
char c; //偏移量 == 2 + 2
//结构体大小 == 最后一个偏移量 4 + 最后一个成员的大小 1,调整为6
};
struct T
{
short a; //偏移量0
struct
{
char b; //偏移量 == 0 + 2,调整为4
int c; //偏移量 == 4 + 1,调整为8
} tmp;
int d; //偏移量 8 + 4,
// 结构体大小 == 12 + 4
};
struct T
{
float f; //偏移量0
char p; //偏移量 == 0 + 4
int arr[3];//展开为3个
//偏移量 == 4 + 1,调整为8
//偏移量 == 8 + 4
//偏移量 == 12 + 4
//结构体大小 = 16 + 4
};
展开
sizeof计算结构体大小:
并不会简单的把每个成员的大小相加,因为它会考虑内存对齐问题,以便以空间换取寻址速度
因此它在计算时会遵守几个规则:
- 偏移量必须为遍历到的所有成员大小的整数倍
- 结构体大小必须是所有成员大小的整数倍(最小公倍数)
1
2
char str[] = "Hello, World!";
size_t length = strlen(str);
展开
strlen(…)是函数,
- 要在运行时 才能计算。参数必须是字符型指针(char*)
- 一般用于返回字符串的实际长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Test1()
{
char p[] = "hello";//5,6
cout << "p: " << p << " " << strlen(p) << " " << sizeof(p) << endl;
char p1[] = "hello\0";//5,7
cout << "p1: " << p1 << " " << strlen(p1) << " " << sizeof(p1) << endl;
char p2[] = "hello\\0";//7,8,第一个\是转义\0的,所以它不算字符,它会让后面的\0变为普通的字符
cout << "p2: " << p2 << " " << strlen(p2) << " " << sizeof(p2) << endl;
char p3[] = "hello\\\0";//6,8,第一个\只转义了第二个\,使得第二个\不能再转义后面
cout << "p3: " << p3 << " " << strlen(p3) << " " << sizeof(p3) << endl;
char p4[] = "hel\0lo";//3,7
cout << "p4: " << p4 << " " << strlen(p4) << " " << sizeof(p4) << endl;
char p5[] = "hel\\0lo";//7,8
cout << "p5: " << p5 << " " << strlen(p5) << " " << sizeof(p5) << endl;
}
展开
注意:strlen 计算的是字符串的实际长度,遇到\0(空字符)即停止;sizeof 计算整个字符串所占内存字节数的大小,当然\0也要+1计算
memcpy
按字节内存拷贝(memory copy),无需通过for循环拷贝了
void * memcpy ( void * destination, const void * source, size_t num );
- 目标数组(复制到这里)
- 源数组(从这里复制数据)
- 字节数
1
2
3
4
5
6
int arr1[10] = { 1,2,3,4,5,6,7 };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 7 * 4);//arr2 : 1,2,3,4,5,6,7
int arr1[10] = { 1,2,3,4,5,6,7 };//1,2,3,4,5,6,7,0,0,0
int arr2[10] = { 0 };
memcpy(arr2, arr1 + 3, 7 * 4);//arr2 : 4,5,6,7,0,0,0,会跳过前3个元素,复制后面的7个元素
展开
如果src不足num个,它会导致读取未初始的内存,如果des不足num个,会导致缓冲区溢出
malloc、free、calloc、realloc
1
2
void* malloc(size_t size);//参数是字节数量,返回无类型的指针
int* p = (int*)malloc(40);//分配40个字节空间,强制转换为int,相当于int arr[10];
展开
malloc(memory allocation内存分配):C 风格内存分配,如果开辟成功,则返回内存指针,如果失败,返回nullptr指针,
1
2
3
void free (void* ptr);//ptr必须要是动态分配的
free(p);//避免内存泄漏
p = NULL;//避免空悬指针
展开
需要用free释放内存
1
void* calloc(size_t num, size_t size);
展开
calloc(contiguous allocation动态内存分配并清零):为 num个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0
1
void* realloc(void* ptr, size_t size);//
展开
realloc: 对动态开辟内存大小的调整,ptr指向被调整的旧内存起始地址,size调整后的新大小,返回调整后的新内存起始地址