突破•指针二

复习review❤️

内存单元的编号 == 地址 == 指针
指针变量是存放地址的变量,日常所说的指针一般是指针变量。

野指针🫧

野指针就是指向的位置无法被正确找到的指针变量。
野指针的成因:

  1. 指针变量未初始化(会被系统随机分配一个值,不是自己想要的)
  2. 指针变量越界访问
  3. 指针变量指向的空间已释放

避免野指针:

  1. 指针变量初始化【如果不知道指向哪,就赋值给NULL。NULL的值是0,0也是地址,但这个地址无法被访问,读取(解引用*)时会报错。所以使用指针前应判断它是否为NULL。】
  2. 小心指针越界
  3. 指针变量不再使用时,及时置NULL,指针使用前检查有效性【防止指向已释放的空间。一个约定俗成的规则:只要是NULL 指针就不去访问。】
  4. 避免返回局部变量的地址【防止指向已释放的空间,因为局部变量的地址在使用完后就会被释放。局部变量存储在函数栈区,当程序调用结束后,在函数栈区的所有东西将会由计算机进行销毁。】

assert断言🫧

assert(正确的条件)是一个宏,常被称为“断言”,使用要加头文件assert.h
作用:在运行时确保程序符合指定条件。如果符合,它不会产生任何影响;如果不符合,就报错终止运行。

如果符合,就像:
pa确实不为NULL,正确,继续执行。
在这里插入图片描述
在这里插入图片描述

如果不符合,就像:
不符合断言的条件pa == NULL,报错,终止执行。
在这里插入图片描述
报错会显示assert所在行号以及错误原因。
在这里插入图片描述

由上可知,符合assert()括号内的才会继续执行。

assert的作用是排查代码错误。
assert的缺点是它作为额外的检查,会增加程序运行的时间。
assert只能在Debug版本中使用,因为在Release版本中会被直接优化掉。

assert的神奇之处

它有一种无需更改代码就能开启或关闭assert()的机制。
如果已经确认程序没有问题,不需要再做断言,就在#include<assert.h>前定义一个宏NDEBUG。如果需要再使用assert(),可以把#define NDEBUG注释或删掉。
在这里插入图片描述

指针的使用和传址调用🫧

实参传递给形参的时候,形参会单独创建一份临时空间来接收实参,对形参的修改不影响实参。即形参与实参只是值相同,但地址不同。
实参是被传过去的,形参是用来接收的。
传值调用:无法在被调用函数中改变主调函数中的变量值。
传址调用:可以在被调用函数中改变主调函数中的变量值。
在这里插入图片描述

数组名的理解🫧

数组名就是数组首元素(第一个元素)的地址,但有两种情况例外:

  1. sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素
    的地址是有区别的)。

理解整个数组和数组首元素地址的区别

在这里插入图片描述

输出结果:
在这里插入图片描述
这里涉及十六进制转化为十进制的计算,我们先不做讨论,主要理解指针。

这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是首元素的地址,+1就是跳过一个元素。

但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
在这里插入图片描述

使用指针访问数组🫧

arr[i] == *(arr+i) == *(i+arr) == i[arr]
知道它们等价就好,不提倡装这个逼啊🙅‍♀️。
数组方式用arr[i]
指针方式用*(arr+i)

一维数组传参的本质🫧

不可以把数组传给一个函数后,在函数内部求数组的元素个数哦。因为本质上数组传参传递的是数组首元素的地址。

#include <stdio.h>

void test(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]); 
	printf("sz2 = %d\n", sz2);
}

int main() 
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);//首元素的地址除以首元素的地址
	return 0;
}

运行结果:
在这里插入图片描述
void test(int arr[])也可以写成void test(int* arr),即一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

二级指针🫧

指针变量也是变量,是变量就有地址。
一级指针*p的地址存放在二级指针**pp中,二级指针**pp的地址存放在三级指针***ppp中,以此类推。
一级指针的类型是int*,二级指针的类型是int**,三级指针的类型是int***,以此类推。

int a = 10;
int* p = &a;
int** pp = &p;
int*** ppp = &pp;
...

二级指针的应用:

#include <stdio.h>

int main() 
{
	int a = 10;
	int* p = &a;
	int** pp = &p;
	**pp = 20;
	printf("%d\n", a);
	return 0;
}

运行结果:
**pp先通过*pp找到p,然后对p进行解引用操作:*p,找到a
在这里插入图片描述
可以把这一级级指针看成是抽屉,里面放着钥匙🗝️,解引用就是拿到钥匙开锁🔓的过程。
理解二级指针的两个*
在这里插入图片描述
可类推,如:
在这里插入图片描述

能量站😚

别赶路,去感受路。
请添加图片描述

❤️❤️❤️ 恭喜! 恭喜! 闯关成功! ❤️❤️❤️

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值