内存是计算机 CPU 执行程序,临时数据存储空间和程序运行空间
- RAM 内存,家用电脑的内存基本上在 8 ~ 16G
- ROM 硬盘,永久数据存储器
计算机的最小单元 CPU + 内存
计算机操作最小数据单位是【字节】
计算机内存地址,就是按照计算机最小数据单元【字节】,对内存中的每一个字节进行编号,从 0 开始到内存最大值。例如 4GB ,它的内存地址范围是 0 ~ 4 * 1024 * 1024 * 1024 == 0 ~ 4294967295,通常情况下,计算机为了更好地管理内存和展示内存地址,内存地址都会采用十六进制的方式来处理,因此 4GB 的内存地址范围为 0x0 ~ 0xFFFF FFFF
内存编号:每一个字节都是一个编号
对于程序而言,每一个字节对应内存都有一个唯一的编号,类似于一条街上,每一个区域都有一个门牌号。且有且只有一个,具有唯一性。
内存编号的唯一,也是方便 CPU 对于内存中的数据进行管理,并且 CPU 可以根据内存地址,快速访问目标内存数据,更具有效率性。
NULL 是内存中编号为0的字节,对应的地址是 0x0
NULL 对应的内存受到系统保护,任何程序,不得从 NULL 中【读取数据】或者【写入数据】,一旦操作,系统直接杀死程序,程序退出。在 C/C++ 代码中常见错误提示为【段错误(核心已转储)】
NULL 一般用于指针变量初始化操作,作为作物提示方便后续操作,如果指针变量在后续代码运行过程中,没有被重新赋值情况下直接使用,会报错提示。因此初始化操作是为了防止【野值】出现。
数据类型 *指针变量名称 = 初始化数据;
- 数据类型 *
- 表示当前定义的变量是一个指针变量,存在指针标记 * 号
- 数据类型告知当前指向内存空间对应的具体数据类型,例如 int *,可以认为这里定义的是一个 int 类型指针变量,也可以认为当前指针指向的内存空间对应的数据类型为 int 类型。
- 指针变量名称:就是一个变量名,需要符合命名规则(小驼峰/首字母大写)
- 指针变量:存储内存空间首地址
- 初始化数据:
- 可以取地址目标变量/内存,或者赋值 NULL 作为初始化数据
- 数据必须一个地址,通过取地址运算符或者其他方式得到地址数据,赋值给指针变量
- 如果尚未明确指向的空间是哪一个,通常情况下,指针初始化为NULL(0x0)
int main(int argc, char const *argv[])
{
/*
这里定义了一个 int 类型的指针变量 p1
1. p1 可以存储一个 int 类型变量内存空间地址
2. p1 指向的内存空间对应的数据为 int 类型
*/
int *p1 = NULL;
return 0;
}
取地址运算符 &
取值运算符 *
案例代码
#include
int main(int argc, char const *argv[])
{
// 数据类型 * 指针变量 = 初始化数据;
/*
定义了一个 int 类型的指针变量,指针变量名为 p
目前初始化为 NULL
*/
int *p = NULL;
// 定义了一个 int 类型变量,变量名 num ,
// 所在内存空间是【栈区】
int num = 10;
// & 取地址操作,可以获取变量在内存空间中的首地址
printf("&num = %p\n", &num);
// 指针变量 p 存储 num int 类型变量空间首地址
p = #
// 查看 p 指针变量存储的地址情况
printf("p = %p\n", p);
// 取地址运算符,可以通过指针变量存储的地址,访问对应内存取值存储数据
printf("num = %d\n", num);
printf("*p = %d\n", *p);
int p1 = "ABCD";
printf("*p1 = %d\n", p1); // 1,145,258,561
return 0;
}
在 64 位 系统环境中,占用 8 个字节,在 32位 系统环境中,占用 4 个字节
int main(int argc, char const *argv[])
{
// 各种类型指针变量定义
short *p1 = NULL;
int *p2 = NULL;
long *p3 = NULL;
float *p4 = NULL;
double *p5 = NULL;
char *p6 = NULL;
printf("sizeof(p1) = %ld\n", sizeof(p1)); // 8字节
printf("sizeof(p2) = %ld\n", sizeof(p2)); // 8字节
printf("sizeof(p3) = %ld\n", sizeof(p3)); // 8字节
printf("sizeof(p4) = %ld\n", sizeof(p4)); // 8字节
printf("sizeof(p5) = %ld\n", sizeof(p5)); // 8字节
printf("sizeof(p6) = %ld\n", sizeof(p6)); // 8字节
return 0;
}
#include
void print_int_array1(int arr[], int capacity);
void print_int_array2(int arr[], int capacity);
int main(int argc, char const *argv[])
{
int arr[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
/*
直接展示数组名数据情况,数组名是一个指针变量,
存储对应的数组空间的【首地址】
*/
printf("arr = %p\n", arr);
printf("&arr[0] = %p\n",&arr[0]);
printf("arr + 5 = %p\n", arr + 5);
printf("--------------------------\n");
print_int_array1(arr, 10);
printf("--------------------------\n");
print_int_array2(arr, 10);
return 0;
}
void print_int_array1(int arr[], int capacity)
{
// 数组操作
for (int i = 0; i < capacity; i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}
}
void print_int_array2(int arr[], int capacity)
{
// 指针操作
for (int i = 0; i < capacity; i++)
{
printf("*(arr + %d) = %d\n", i, *(arr + i));
}
}
arr[5] <==> *(arr + 5)
arr = 0x7fffd9e7ca40
arr + 5 = 0x7fffd9e7ca54
/*
发现地址多出了 20 个字节
ca54 - ca40 ==> 20
16进制 16进制 10进制
arr 是一个 int 类型的指针,指针存储的地址对应的内存空间对于 CPU 而言是一个 int 类型。
arr + 5 操作不是在原本的地址移动 5 个字节数,而是移动 5 个 int 类型数据
arr + 5 ==> arr地址 + 5 * sizeof(int) ==> arr地址 + 20
假设 arr 地址时 0x1000
最终 arr + 5 对应的地址是 0x1014
*(arr + 5) 获取 arr + 5 对应地址元素,等价于 arr[5] 操作
*/
#include
void swap1(int n1, int n2);
void swap2(int *p1, int *p2);
int main(int argc, char const *argv[])
{
int num1 = 10;
int num2 = 20;
printf("Before num1 = %d, num2 = %d\n", num1, num2); // 10 20
// 值传递 仅对数值传递, swap1 函数中的任何操作对于 num1 和 num 2 没有任何影响
swap1(num1, num2);
printf("After num1 = %d, num2 = %d\n", num1, num2); // 10 20
printf("------------------------------------\n");
printf("Before num1 = %d, num2 = %d\n", num1, num2); // 10 20
// 址传递 取地址的 num1 和 num2 作为函数的参数
swap2(&num1, &num2);
printf("After num1 = %d, num2 = %d\n", num1, num2); // 20 10
return 0;
}
void swap1(int n1, int n2)
{
int temp = n1;
n1 = n2;
n2 = temp;
}
void swap2(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
#include
int main(int argc, char const *argv[])
{
int num = 100;
// 一级指针,存储 int 类型变量的空间首地址
int *p = #
// 二级指针 存储 int 类型指针变量空间首地址
int **q = &p;
// num 空间首地址
printf("&num = %p\n", &num);
printf("p = %p\n", p);
// 相当于根据二级指针存储的 一级指针变量首地址,取值一级指针存储的地址
printf("*q = %p\n", *q);
printf("------------------------\n");
// num 存储的数据情况
printf("num = %d\n", num);
printf("*p = %d\n", *p);
printf("**q = %d\n",**q);
return 0;
}
- 首先是一个数组
- 数组中的每一个元素都是一个指针
#include
int main(int argc, char const *argv[])
{
int num1 = 100;
int num2 = 200;
int num3 = 300;
// 定义了一个 int 类型指针数组,容量为 3
// 每一个元素都是一个 int 类型的指针。
int * arr[3] = {NULL};
// 将变量取地址存储操作,存储到对应的数组中
arr[0] = &num1;
arr[1] = &num2;
arr[2] = &num3;
//【重点】数组中每一个元素,存储的内容都是地址
printf("arr[0] = %p\n", arr[0]);
printf("arr[1] = %p\n", arr[1]);
printf("arr[2] = %p\n", arr[2]);
printf("\n");
// 【重点】通过数组中的指针取值操作,获取对应变量的数据
printf("*arr[0] = %d\n", *arr[0]);
printf("*arr[1] = %d\n", *arr[1]);
printf("*arr[2] = %d\n", *arr[2]);
printf("\n");
// 【重点】利用数组名通过累加的方式获取数组元素存储地址对应的变量数据内容
printf("**(arr + 0) = %d\n", **(arr + 0));
printf("**(arr + 1) = %d\n", **(arr + 1));
printf("**(arr + 2) = %d\n", **(arr + 2));
printf("\n");
printf("**(arr + 0) = %x\n", **(arr + 0));
printf("**(arr + 1) = %x\n", **(arr + 1));
printf("**(arr + 2) = %x\n", **(arr + 2));
printf("\n");
printf("*(arr + 0) = %p\n", *(arr + 0));
printf("*(arr + 1) = %p\n", *(arr + 1));
printf("*(arr + 2) = %p\n", *(arr + 2));
printf("\n");
printf("arr[0] = %p\n", arr[0]);
printf("*arr + 5 = %p\n", *arr + 5);
return 0;
}