【C语言】指针2——高级指针

高级指针
Ciallo~ (∠・ω< )⌒★

在这里插入图片描述

1. 野指针

野指针是指:指向已释放或无效内存地址的指针。当一个指针所指向的内存被释放后,如果该指针没有被置为 NULL 或者重新指向有效的内存,那么这个指针就成为了野指针。使用野指针可能会导致程序出现未定义的行为,例如访问非法内存地址,导致程序崩溃或者产生错误的结果。

可能出现野指针的操作:

  1. 如果一个指针没有被初始化就被使用,它也可能成为野指针,因为它指向的内存地址是不确定的。
  2. 局部变量超出作用域,如数组越界
  3. 释放内存后未置空指针
int *ptr = (int*)malloc(sizeof(int));
free(ptr);  // 内存释放,但 ptr 仍指向原地址
*ptr = 10;  // 危险!操作已释放的内存(野指针)

如何避免出现野指针:
4. 使用空指针(NULL)初始化指针变量
5. 在指针释放后将其设置为NULL
6. 确保指针始终指向有效的内存地址
7. 检查指针的有效性后再进行访问(assert断言)

2. const 修饰指针

按照 const 与 * 的相对位置,可分为:

2.1 const int* p

const 在 * 的左边,说明 const 是用来修饰 *p 的,*p 不能被改变,保证指针指向的地址所存放的值不能通过指针来改变,但指针指向的地址是可以被改变的。

2.2 int* const p

const 放在了 * 的右边,此时const是用来修饰 p 的,即 p 不能被改变,而 *p 可以被改变。

2.3 const int* const p

这种情况下,* 的左右两边都有 const,则 *p 和 p 都不能被改变。

3. void 指针

void 指针也叫通用指针,当我们不确定一个指针将要指向的数据类型时,可以先声明为 void 类型指针。用到时,将其强制类型转换为需要的类型即可。这类指针通常在编写函数时用到。比如qsort函数,内存函数等等。(后面都会单独出详解)

4. 多级指针

一级指针是用来存放内存地址的,那么指针有没有地址呢?有的,兄弟有的。
二级指针就是用来存放一级指针的地址的。同理,三级指针就可以存放二级指针的地址。
我们可以在VS 2022上用调试的方法观察下面的代码:

int main()
{
    int a = 5;
    int* p = &a;
    int** m = &p;
    printf("a是%d\n", a);
    printf("p是%p\n", p);
    printf("*p是%d\n", *p);
    printf("*m是%p\n", *m);
    printf("**m是%d\n", **m);
    return 0;
}

调试:
在这里插入图片描述
我们可以清晰地看到 a 和 p 的地址
运行结果如下:
在这里插入图片描述
因为 p 存放着 a 的地址,所以 p 的值是 0000009484F3FA14,解引用拿到 a 的值,所以 *p 是 5
因为 m 存放着 p 的地址,解引用拿到 p 的值,所以 *m 是 5,则再解引用,拿到 a 的值,所以 **m 是 5

5. 结构体指针

废话不多说,上代码:

struct stu
{
    char name[20];
    int age;
};
int main()
{
    struct stu m = { "FlamePolemo",18 }; //定义了一个结构体
    struct stu* p = &m; //定义一个结构体指针,存放它的地址
    printf("%s\n", (*p).name); //通过指针访问该结构体
    printf("%d\n", (*p).age);
    return 0;
}

运行结果:

在这里插入图片描述

这里再联系一种指针访问结构体的方法:-> 操作符
仍然是这段代码:

struct stu
{
    char name[20];
    int age;
};
int main()
{
    struct stu m = { "FlamePolemo",18 }; //定义了一个结构体
    struct stu* p = &m; //定义一个结构体指针,存放它的地址
    printf("%s\n", (*p).name); //通过指针访问该结构体
    printf("%d\n", (*p).age);
    printf("%s\n", p->name); //通过->操作符访问结构体
    printf("%d\n", p->age);
    return 0;
}

运行结果:

在这里插入图片描述

6. assert 断言

assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。
使用方法如下:

#include <assert.h>
int main()
{
    int* p;//p是野指针
    assert(p);
    return 0;
}

运行结果:

在这里插入图片描述

哈哈,如聪明的你所料,根本运行不了

所以我们使用指针前,先用 assert 断言检测指针的有效性,就可以避免野指针的出现。它也可以多个指针一起检测,比如

#include <assert.h>
int main()
{
	int* p;
	int* m;
	assert(p && m);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值