八、内存、指针、数组和指针、值传递和址传递、二级指针

本文详细讲解了指针、数组、内存地址的基础知识,包括内存分区、NULL概念、指针运算符、内存分配、数组名与指针的关系,以及值传递和址传递的区别。还探讨了二级指针和指针数组的实例,帮助读者掌握C/C++编程中这些关键概念的运用。
摘要由CSDN通过智能技术生成

指针、数组和指针、二级指针

1. 内存
1.1 什么是内存

内存是计算机 CPU 执行程序,临时数据存储空间和程序运行空间

  • RAM 内存,家用电脑的内存基本上在 8 ~ 16G
  • ROM 硬盘,永久数据存储器

计算机的最小单元 CPU + 内存

1.2 内存的分区【重点】

在这里插入图片描述

1.3 内存地址

计算机操作最小数据单位是【字节】

计算机内存地址,就是按照计算机最小数据单元【字节】,对内存中的每一个字节进行编号,从 0 开始到内存最大值。例如 4GB ,它的内存地址范围是 0 ~ 4 * 1024 * 1024 * 1024 == 0 ~ 4294967295,通常情况下,计算机为了更好地管理内存和展示内存地址,内存地址都会采用十六进制的方式来处理,因此 4GB 的内存地址范围为 0x0 ~ 0xFFFF FFFF

内存编号:每一个字节都是一个编号

对于程序而言,每一个字节对应内存都有一个唯一的编号,类似于一条街上,每一个区域都有一个门牌号。且有且只有一个,具有唯一性。

内存编号的唯一,也是方便 CPU 对于内存中的数据进行管理,并且 CPU 可以根据内存地址,快速访问目标内存数据,更具有效率性。

1.5 NULL

NULL 是内存中编号为0的字节,对应的地址是 0x0

NULL 对应的内存受到系统保护,任何程序,不得从 NULL 中【读取数据】或者【写入数据】,一旦操作,系统直接杀死程序,程序退出。在 C/C++ 代码中常见错误提示为【段错误(核心已转储)】

NULL 一般用于指针变量初始化操作,作为作物提示方便后续操作,如果指针变量在后续代码运行过程中,没有被重新赋值情况下直接使用,会报错提示。因此初始化操作是为了防止【野值】出现。

2. 指针
2.1 指针的基本格式
数据类型 *指针变量名称 = 初始化数据;
  • 数据类型 *
    • 表示当前定义的变量是一个指针变量,存在指针标记 * 号
    • 数据类型告知当前指向内存空间对应的具体数据类型,例如 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;
}
2.2 指针操作重点运算符★

取地址运算符 &

  • 取地址运算符,可以获取对应变量在内存空间中【首地址】

取值运算符 *

  • 取值运算符,通过指针变量存储的地址,CPU 访问地址对应的变量,取值变量数据

案例代码

#include <stdio.h>

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 = &num;

    // 查看 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;
}
2.3 指针变量占用的内存空间

在 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;
}
3. 数组和指针【重点】
3.1 数组名就是一个指针变量
#include <stdio.h>

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));
    }    
}
5.2 数组名 + 下标方式操作

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] 操作
*/
6. 值传递和址传递

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>

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;
}
7. 二级指针
7.1 二级指针基础案例

在这里插入图片描述

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int num = 100;
    // 一级指针,存储 int 类型变量的空间首地址
    int *p = &num;
    // 二级指针 存储 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;
}
7.2 指针数组
  • 首先是一个数组
  • 数组中的每一个元素都是一个指针
#include <stdio.h>

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;
}
  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值