【c语言】复习无止境,day3--指针篇

本文详细介绍了C语言中的指针概念,包括指针的定义、作用、使用方法、指针运算、const与指针、void指针、数组指针与指针数组、二级指针以及函数指针。特别强调了指针在函数参数传递和内存管理中的重要角色,以及如何避免空指针和野指针问题。同时,讨论了指针越界的原因和malloc函数的设计考虑。
摘要由CSDN通过智能技术生成

什么是指针:
指针是一种特殊的数据类型,使用指针可以定义指针变量,指针变量中存储的是整形数据,代表了内存的编号,可以这个编号来访问对应的内存。

为什么要使用指针:
1、函数之间是相互独立的,但是有时候需要共享变量
传参是单向值传递
全局变量命名容易冲突
使用数组还需要传递长度
虽然命名空间相互独立,但是地址空间是同一个,所以指针可以解决共享变量的问题
2、由于函数之间传参是值传递(内存拷贝),对于字节数较多的变量,值传递的效率较低,如果传递变量的地址只需要传递4|8个字节
3、堆内存无法取名字,它不能像data/bss/stack让变量名与内存之间建立联系,只能使用指针来记录堆内存的地址,以此来使用堆内存

如何使用指针:
定义:
类型* 变量名_p;
1、指针变量与普通变量的用法区别很大,建议在取名时以p结尾加以区分
2、指针变量的类型表示了存储的是什么类型的变量的地址,它决定了通过这个指针变量可以访问的字节数
3、一个只能定义一个指针变量
int
p1,p2,p3; // 只有p1是指针变量,p2 p3都是int类型变量
int *p1,p2,p3;// p1 p2 p3都是指针变量
4、指针变量与普通变量一样默认值是随机的,一般初始化为NULL
赋值:
变量名_p = 地址; // 必须是有权限的而且有意义的内存地址
指向栈内存:
int num;
int
p = #
指向堆内存:
int
p = malloc(4);
解引用:
*p;
*p <==> num
通过指针变量中记录的内存编号去访问内存,这叫做解引用。
该过程可能产生段错误,原因是指针变量中存储的是非法的内存编号
注意:访问的字节数是由指针变量的类型决定的

作业:
1、实现一个变量交换的函数,调用它对一个数组进行排序
void swap(int* p1,int* p2)
2、实现一个函数计算两个整数的最大公约数和最小公倍数,return来返回最大公约数,最小公倍数用指针处理
3 6 3
3 6 6

使用指针需要注意的问题:
空指针:值为NULL的指针变量叫做空指针,对空指针进行解引用一定会产生段错误
NULL也是一种错误标志,如果一个函数的返回值是指针类型时,当函数执行出错时可以返回NULL作为错误标志
NULL作为指针变量的初始值 if(NULL == p)
如何避免空指针带来的段错误:
对于来历不明的指针使用前,一定要进行判断
1、调用函数返回值为指针类型时,可能是空指针
2、当函数的参数是指针类型时,调用者传递过来的可能是空指针
注意:NULL在绝大多数系统中是0,极个别系统中是1,if(!p)

野指针:指向不确定的内存的指针
对野指针解引用的后果:
1、段错误
2、脏数据
3、一切正常
野指针比空指针的危害性要更严重,因为它是无法判断出来的,错误可能是隐藏型的,短时间内可能不会暴露
所有的野指针都是人为制造的,如何避免产生野指针:
1、定义指针时一定要初始化 int* p = NULL;
2、函数不返回局部变量的地址
3、指针变量指向的内存被释放后,指针变量要及时置空 =NULL

指针的运算:
指针变量中存储的是整数,理论上整数支持的所有运算指针变量都支持,但是绝大多数的运算是无意义
指针 + n:指针+指针类型字节数n 前进了n个元素
指针 - n:指针-指针类型字节数
n 后退了n个元素
指针 - 指针:(指针-指针)/指针类型字节数 计算出两个指针之间间隔了多少个元素

const与指针:
当我们为了提高传参效率而使用指针时,传参的效率提高了,但是变量也有被修改的风险,这种情况可以使用const来进行保护
const int* p; 保护指针所指向的内存不被修改
int const* p; 同上
int* const p; 保护指针的指向不能修改
const int* const p; 保护指针指向的内存和指向都不能修改
int const* const p; 同上

void指针:
1.以一个字节为单位移动
2.不能解引用
3.可以与任何类型指针自动类型转换(c语言)
4.一般做函数的参数,返回值
数组指针与指针数组:
指针数组:
是由指针变量组成的数组,它的成员都是指针变量
类型* arr[长度]
数组指针:
专门用于指向数组的指针
类型 (arrp)[长度];
int (arrp)[5]=&arr;指针指向数组的地址,&arr+1的一步的宽度为五个int长度;
&arr类型 int (
)[]
arr 类型 int

数组名与指针:
数组名其实是一种特殊的指针,但它是常量,不能修改它的值,它与内存之间是映射关系,不能改变,而指针与内存之间是指向关系,可以改变

数组名是没有存储空间,而指针变量是有存储空间
指向数组的指针,可以当做数组名使用
数组名也可以当做指针使用
数组名[i] == *(数组名+i)
*(指针名+i) == 指针名[i]

注意:当数组作为参数传递时变成了指针,所以长度丢失了

二级指针:
二级指针就是指向指针的指针,里面存储的是指针变量的地址。
定义:
类型** 变量名_pp;
赋值:
变量名_pp = &指针变量;
解引用:
*变量名_pp <=> 指针
**变量名_pp <=> *指针

函数指针:
函数名就是地址(整数),它代表了函数在代码段中的位置
函数指针就是指向函数的指针,里面存储的是函数在代码段中的位置
函数会反翻译二进制指令存储到代码段,而函数名就这段指令的首地址。
可以定义特殊的指针变量存储函数的首地址,这样就可以把函数当前数据进行传递。
我们把存储函数地址的指针变量叫做函数指针。
如何定义函数指针:
1、复制函数的声明语句
2、给函数名加上小括号并在函数名前加一个*
3、给函数名重新取函数指针变量名

当我们把函数作为数据传递时,被调用的函数可以通过函数指针调用我们以参数形式提供的函数,这种模式叫回调模式,比如:qsort、bsearch。
由于函数指针的类型比较长,一般会选择使用 typedef 进行类型重定义。
void func(int,int);
typedef void (*FP)(int,int);

int * p=malloc(4);
1.为什么可以越界那么多?
因为当指针申请开辟堆区空间时,操作系统为了防止频繁申请而降低效率,
将33页(一页4M)空间一次性开辟给malloc管理使用
2.为什么是33790才产生段错误?
4
(33790+1)=135164 3341024=135168 当33791的时候九越界段错误了
3.为什么这样设计malloc函数?
为了提高操作系统的工作效率,防止多次申请开辟将时间用于处理多条相同语句。*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值