C语言的指针(个人理解)

指针

指针是一种特殊的数据类型!!!指针是一种特殊的数据类型!!!指针是一种特殊的数据类型!!!


一、指针是什么?

指针是一种数据类型,使用它可以定义指针变量,这种变量里存储是整数,这种整数代表了内存的编号,每个整数对应一个字节,使用指针变量可以访问对应的内存,具体访问的多个字节由指针的类型决定。

二、指针使用条件

注意:从理论上讲指针可以访问任何位置的内存,但绝大部分的内存我们没有权限访问,因此非常容易产生段错误,因此建议只有合适的时候才使用指针。

函数之间共享局部变量:
由于全局变量浪费内存,还可能造成命名冲突,所有全局变量不适合大量共享。
而函数传递默认是值传递,无法共享,所以指针是函数之间共享局部变量的最好选择。

提高函数的传递效率:
而函数传递默认是值传递(内存拷贝),当变量的字节数较多时,传递变量的地址效率更高,只需要4/8字节。
但这样就带来了变量被修改的风险,可以使用const配合指针来保存变量。

配合堆内存使用:
堆内存无法取名(不能与标识符建立映射关系),因此必须与指针配合使用。

三、如何使用指针

如何使用指针:
定义指针变量:类型* 指针变量_p;

例:int* p;

1、由于指针变量的用法与普通变量不同,为了避免与普通变量混淆,指针变量一般以p或_p结尾。
2、指针变量不能连续定义

int* p1,p2; // p1 是指针,p2 是int类型变量
int *p1,*p2; // p1、p2 都是指针

3、指针变量与普通变量一样默认值是随机(野指针),为了安全一定初始化,如果不知道赋值什么值,可以赋值为NULL(空指针)。

4、指针的类型决定了访问内存时的字节数。
指针变量的赋值:

用变量的地址赋值:p = #
用堆内存地址赋值:p = malloc(sizeof(*p));

指针变量的解引用:*指针变量

例:
int num = 3;
int* p = #
printf("%d",*p);//*p指针解引用

通过指针变量中存储内存编号去访问内存,具体访问多个字节由指针变量的类型决定。
该过程可能产生段错误,但要从指针变量赋值的步骤寻找原因。

四、指针的运算

指针变量中存储的是整数,理论上整型数据能使用的运算符,指针变量都可以使用,但只有以下运算有意义:
指针 + n <=> 指针 + (n * 字节数)
指针 - n <=> 指针 - (n )
指针 - 指针 <=> (指针-指针)/字节数 只有类型相同的指针才可能相减。

五、使用指针要注意的问题

空指针

指针变量的值为NULL的指针被称为空指针,这种指针不能解引用,否则会产生段错误。
大多数系统的NULL是0地址,可以把指针变量当逻辑值使用,但不建议这样。
为了避免空指针导致的段错误,当使用来历不明的指针时应判断是否是空指针。
if(NULL == p)
{
}

野指针

指针变量的值是随机的、不确定,这种指针称为野指针,使用野指针不一定立即出错,也不一定会出错,与空指针相比它的危害更大。
无法判断一个指针变量是否是野指针,所有为了避免野指针导致的Bug,我们应该避免产生野指针。
1、定义指针变量时一定要初始化。
2、指向变量所指向的内存被释放、销毁后,指针变量要及时赋值为NULL。
3、函数不返回局部变量的地址。

六、指针与const

const int* p; //保护目标不被*p修改。
int const *p; //同上
int* const p; //保存指针变量p不被修改
const int* const p; //既保存p也保存*p
int const * const p; //同上

七、函数指针

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

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

void func(int,int);
typedef void (*FP)(int,int);

八、二级指针

一级指针里面存储普通变量的地址,二级指针里面存储的是一级指针变量的地址。
当跨函数共享指针变量时,需要使用二级指针。
定义二级指针:
类型** 变量名_pp;

二级指针的赋值

int num;
    int* p = &num;
    int** pp = &p;

二级指针解引用:

    *pp <=> p;
    **pp <=> *p;

九、指针与数组名的关系

数组名就是数组空间的首地址,是一个常量地址,可以把它看作常指针。
数组名可以使用解引用的语法访问成员,指针也可以使用[]解引用,所以:
arr[n] <=> *(arr+n);
与普通指针的区别:
1、数组名是常量,普通指针是变量
2、普通指针变量有自己的存储空间,用来存储内存编号,它与目标内存是指向关系。
3、数组名没有自己的存储空间,它就代表数组空间的首地址,它与数组内存是映入关系。
4、对数组名取地址结果还是数组名的值。

  int arr[5]
        arr <=> &arr;
        arr 类型 int* 
        &arr 类型 int (*)[5]

总结

指针很重要的,刚开始用指针的时候我非常不习惯,无休无止的段错误差点使我崩溃,这时候就需要多打代码,习惯之后,你就会发现指针非常神奇, 但指针不能随便使用,详见本文的第二条和第五条。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值