C语言-指针详解篇

本文详细介绍了指针的概念、作用,包括指针变量、地址运算、不同类型指针的特性,以及如何避免野指针、使用指针访问数组和函数、数组传参的本质等。此外,还涵盖了二级指针、字符指针、数组指针、函数指针和回调函数等内容。
摘要由CSDN通过智能技术生成

目录

对象和地址

 指针

指针变量大小

结论:

指针变量类型的意义

结论

指针+-整数

结论

const修饰指针

结论:

指针运算

指针+-整数

指针-指针

指针的关系运算

野指针

概念:

成因:

如何规避野指针:

assert断言

使用指针访问数组

数组名的理解

例外情况:

结论:

使用指针访问数组

数组传参的本质

总结:

二级指针

指针数组

字符指针变量

数组指针变量

概念:

初始化:

函数指针变量

概念:

创建函数指针变量:

解析:

使用:

typedef关键字

函数指针数组

概念:

定义

回调函数

概念:

总结


       首先,在学习指针之前我们需要明白指针的作用和含义。我在学习指针的时候,一直在想指针到底有什么用处。在理解指针之前我们需要搞懂,指针和指针变量是两回事。

  1. 指针就是地址。
  2. 指针变量用来存放地址。

这两句话很重要,在之后我会详细讲解。

对象和地址

      变量是“保存数值的盒子”,有序地排列在内存空间当中。“变量”拥有多个属性。在之前,我们学习了数据类型长度。例如int类型变量和double类型变量就有不同的长度。可以利用sizeof求得。

//示例一

#include <stdio.h>

int main()
{

     int i = 10;

     double x = 20;

     printf("%d",sizeof(i));

     printf("%d",sizeof(x));
     return 0;

}

在内存空间上,想要能够准确地找到各个变量的“位置”,我们需要地址。

那么就有了取址运算符“&”。取址运算符的功能是取得对象的地址,也就是生成一个指向地址的指针。

int main()
{

     int i = 10;

     double x = 20;

     printf("%p",&i);

     printf("%p",&x);
     return 0;

}

 指针

        只显示对象的地址并没有太大的用处,要想明白指针的用处还是看如下代码。

看这段代码,我们可以大致明确“int型变量”和“指向int型变量的指针变量”的区别也可以明白指针的赋值形式。请看下图。

指针变量大小

结论:

  1. 32位平台下地址是32个比特位,指针变量大小是四个字节
  2. 64位平台下地址是64个比特位,指针变量大小是八个字节
  3. 指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下,大小是相同的。

指针变量类型的意义

结论

指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。

 

指针+-整数

结论

指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
 

const修饰指针

结论:

  1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
  2. const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变

指针运算

指针类型运算分为三种

  1. 指针+-整数
  2. 指针-指针
  3. 指针的关系运算

指针+-整数

指针-指针

指针的关系运算

野指针

概念:

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

成因:

  1. 指针未能初始化。
  2. 指针越界访问。
  3. 指针指向的空间释放。

如何规避野指针:

  1. 如果不知道指针应该指向哪⾥,可以给指针赋值NULL.
  2. 指针变量不再使用时,即时置NULL,指针使用之前检查有效性。

assert断言

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

使用指针访问数组

数组名的理解

例外情况:

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

除了上述两种特例,任何地方使用数组名,数组名就是首元素的地址。

使用指针访问数组

数组传参的本质

示例代码:

由上图代码可以发现,在函数内部是没有正确的获得数组的元素个数。由上个部分学习了:数组名是首元素的地址,那么数组传参的时候,传递的是数组名,也就是本质上数组传参本质上传递的是数组首元素的地址。

所以函数形参的部分,理论上应该使用指针变量来接受首元素的地址。那么在函数内部我们写sizeof(arr)计算一个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是指针,所以在函数内部是没办法求得数组元素个数的。

总结:

一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

二级指针

前面已经学过,指针变量里面存放的是地址,那么指针变量的地址存放在哪里?答案是二级指针里面。

指针数组

指针数组是存放指针的数组。

字符指针变量

代码constant char*pstr="hello world.",本质是把字符串"hello world."首字符的地址放到了pstr中。

数组指针变量

概念:

存放的是数组的地址,能够指向数组的指针变量。

int (*p)[10];

p和*先结合,说明p是一个指针变量,然后指向的一个是大小为10个整形的数组。所以p是一个指针指向一个数组,叫做数组指针。

初始化:

int (*p) [10] = &arr;

 |     |      |

 |     |      | 

 |     |    p指向数组的元素个数

 |   p是数组指针变量名

 p指向的数组的类型

函数指针变量

概念:

函数指针变量是用来存放函数地址的,未来通过地址调用函数的。

创建函数指针变量:

函数名就是函数的地址,当然也可以通过&函数名的方式获得函数的地址。

void test()

{

      printf("hehe\n");

}

void (*pf1)() = &test;

void (*pf2)() = test;

int Add(int x,int y)

{

   return x+y;

}

int (*pf3)(int,int) = Add;

int (*pf3)(int x, int y) = &Add;

解析:

int (*pf3)  (int x, int y)

  |     |       ---------------

  |     |               |

  |     |              pf3指向函数的参数类型和个数的交代

  |  函数指针变量名

pf3指向函数的类型

使用:

int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}

运行结果:
5

8

typedef关键字

typedef是用来类型重命名,可以将复杂的类型,简单化

例如:你觉得 unsigned int写起来不方便,就可以重命名为unit

typedef unsigned int unit

对于数组指针和函数指针稍微有一些区别

例如:我们由数组指针类型int(*)[5],需要重命名parr_t,可以这样写:

typedef int(*parr_t)[5]

函数指针也是如此,比如将void(*)(int)类型重命名pf_t

typedef void(*pfun_t)(int) //新类型必须在*的右边

函数指针数组

概念:

把函数的地址存到一个数组中,那这个数组就叫函数指针数组

定义

int (*parr1[3])();
parr1先和[ ]结合,说明parr1是数组,那么数组的内容就是int(*)()类型的函数指针。

回调函数

概念:

回调函数就是一个通过函数指针调用的函数。

总结

磨蹭了两天,笔力实在有限,对于这类介绍文章实在写不出新东西新感悟。说是详解,肯定还有缺漏之处,还请各位多多指教。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值