Chapter 5 Memory and Pointer
Pointers
NULL就是整数0
Structure member accessing
红色方框中的两种办法等效,是结构指针的访问方式。
Print out the addresses
答案:注意与上面的示意图做对比,同时注意,示意图中的首地址是随便编造的。
图中是9个byte啊?为什么sizeof(pStu)是8?
sizeof(pStu)
、sizeof(Student *)
和 sizeof(Student)
分别代表以下内容:
sizeof(pStu)
:pStu
是一个指向Student
结构体的指针。在64位系统上,指针的大小是 8字节,因此sizeof(pStu)
的结果是8。
sizeof(Student *)
:Student *
也是一个指向Student
结构体的指针。同样地,指针在64位系统上是 8字节,所以sizeof(Student *)
的结果也是8。
sizeof(Student)
:- 这里的
Student
是一个结构体,包含了3个成员:char name[4]
:4个字节。int born
:4个字节。bool male
:1个字节。
理论上,这些成员的总大小是 4 + 4 + 1 = 9字节,但由于内存对齐的原因,编译器可能会对结构体进行填充(padding),以确保数据在内存中对齐到合适的边界上。
在你的例子中,sizeof(Student)
的结果是 12字节,这是因为编译器将bool
类型成员之后的空隙(3字节)填充到4字节对齐。这样做的目的是为了提高内存访问的效率。
- 这里的
Pointers of Pointers
注意右边代表当前方格的地址
同理pointer to pointer 用**就可以访问回去。
Constant pointers
注意三种对比。
const int * p1 = #
:- 解释:这是一个指向常量整数的指针。指针
p1
指向num
,你可以改变指针本身的指向(如指向不同的变量),但是不能通过p1
修改它指向的值。 - 限制:
- 你 不能 修改
*p1
的值(即num
的值),*p1 = 3;
会报错。 - 你可以直接修改
num
的值,如num = 3;
是允许的,因为num
本身并不是常量。
- 你 不能 修改
- 解释:这是一个指向常量整数的指针。指针
int * const p2 = #
:- 解释:这是一个常量指针,指针
p2
只能指向num
,但你可以通过p2
修改num
的值。 - 限制:
- 你 可以 修改
*p2
的值(即num
的值),例如*p2 = 3;
是允许的。 - 你 不能 修改指针
p2
本身的指向,p2 = &another;
会报错,因为p2
是一个常量指针。
- 你 可以 修改
- 解释:这是一个常量指针,指针
const int * const p3 = #
:- 解释:这是一个指向常量整数的常量指针。
p3
既不能改变它指向的值,也不能改变它指向的地址。 - 限制:
- 你 不能 通过
p3
修改num
的值,也不能修改p3
本身的指向。
- 你 不能 通过
- 解释:这是一个指向常量整数的常量指针。
- 函数
foo(const char * p)
:- 解释:这个函数的参数
p
是一个指向const char
的指针,意味着你不能通过p
来修改它所指向的字符数据。试图把p
转换为普通char *
的指针将导致语法错误,因为这样会违反常量指针的规则。
- 解释:这个函数的参数
关于记忆
-
const int * p1
:从右往左看,它是一个指向int
的指针,但指向的int
是常量(不可修改的)。- 翻译为:"p1是一个指向常量整数的指针,不能修改值,但可以改变指向"。
-
int * const p2
:从右往左看,p2
是一个常量指针,指向一个int
,所以不能改变指针的指向,但可以改变int
的值。- 翻译为:"p2是一个常量指针,可以修改值,但不能改变指向"。
-
const int * const p3
:从右往左看,这既是一个常量指针,又指向常量整数。- 翻译为:"p3既不能改变指向,也不能修改值"。
Pointers and Arrays
The addresses of array elements
Array name
注意下面的是把一个指针当作数组来使用。
在C中定义一个数组时,数组名本质上是一个指向该数组第一个元素的指针常量。具体来说,students
是一个指向 Student
类型的指针,指向数组中第一个元素的地址。它的类型是 Student *
。
因此,students
表示的是数组第一个元素 students[0]
的地址。
为什么
Pointer arithmetic
int *p = numbers + 1;
numbers
的类型会被解释为 int *
,指向数组的第一个元素,即 &numbers[0]
。因此,numbers
在这些上下文中可以像指针一样使用。
这里的 numbers + 1
是一个指针运算。数组名 numbers
实际上是指向数组第一个元素的指针,即 &numbers[0]
。而 numbers + 1
则指向数组的第二个元素(numbers[1]
的地址)。所以,p
最终指向的是 numbers[1]
的地址。
p[1] = 30;
p[1]
其实是指针的下标运算,等同于 *(p + 1)
。现在 p
指向的是 numbers[2]
,所以 p[1]
就是 numbers[3]
。因此,p[1] = 30
会将 numbers[3]
的值修改为 30
。此时 numbers
数组的最终内容变为:
numbers[0] = 0
numbers[1] = 10
numbers[2] = 20
numbers[3] = 30
Differences between a pointer and an array
注意第三点,指针和数组的区别。
Lab4 makefile#ex1
Allocate memory: C style
Program memory
注意,本section开始allocate的memory全是在heap里的,看下面的例子。
arm64里的例子请结合上面的图示来一起看。
注意下面申请的memory,我们得到了16个字节,但是12个浪费了,用于对齐。
Memory allocation
注意malloc函数返回void*类型指针,而且是未初始化的
具体来说,malloc
返回的地址是你可以使用的动态内存的第一个字节的地址,也就是分配的内存块的起始地址。返回的这个地址是类型为 void*
的指针,需要根据实际使用的类型进行转换。
Memory deallocation
必须自己手动释放
请看图中的qusetion,第一次申请的memory的地址就彻底消失了,没办法找回,浪费了内存,也没法回收了。
foo()函数也是同样的问题,p是一个局部变量,运行之后不手动free,纳闷会造成大量的memory只被申请没法释放!
但是程序结束之后,操作系统会把内存重新管理,因此上面那个小程序不去释放是没什么问题的,但是在实际应用中必须要申请+释放。
Allocate memory: C++ style
Operator new and new[]
new type会返回一个指针。
下面是其他特性。
Operator delete and delete[]
- 数组内存的释放:
- delete pa1;
- 虽然用于释放通过 new[]
分配的数组的内存,但这是错误的用法。应该使用 delete[]
来释放数组,否则可能导致未定义行为。
- delete [] pa2;
- 这是正确的释放通过 new[]
分配的数组内存的方式。
- 调用析构函数:
delete psa1;
- 释放数组的内存,并调用数组第一个元素的析构函数。这个用法适用于分配了单个对象时,尽管这里的注释提到了数组(可能是误导)。
delete [] psa2;
- 释放整个数组的内存,并调用所有数组元素的析构函数。这个用法是正确的,当使用
new[]
分配对象数组时,应该使用delete[]
来确保所有元素的析构函数都被正确调用。
- 释放整个数组的内存,并调用所有数组元素的析构函数。这个用法是正确的,当使用