【C语言】- 指针进阶版(数组指针,数组传参,函数指针数组等)

文章开头,先梳理一下指针的基本知识

1 .指针是一个变量,用来存放地址。
2 .指针的大小是固定的4/8个字节(32位平台/64位平台)。
3 .指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。

字符指针

字符指针char*是指针类型的一种,存放字符变量的地址,一般形式:

    char c = 'a';
    char *p1 = &c;

还有一种特殊的情况就是与字符串相关的时候

    char* p2 = "abcdef";

这里其实是将字符串“abcdef”的首字符a的地址赋给了字符指针p,即通过字符指针我们可以找到一整个字符串,当我们尝试打印这一个字符串时,可以通过指向这串字符串的字符指针
在这里插入图片描述

指针数组与数组指针

指针数组顾名思义就是存放指针的数组。他本质上是一个数组,符合数组的所有性质,只不过里面的元素是指针类型

而数组指针,本质上是指针,指向数组的指针

从定义上分清了这两个概念,我们从代码角度来区别一下
在这里插入图片描述
所以p1为指针数组,而p2是数组指针在这里插入图片描述

从以上两段代码我们了解到,当我们想定义一个数组指针时,由于[ ]的优先级高于 * 号的,所以必须加上()来保证p先和 * 结合。

提起指针,我们已经不陌生,指针就是地址,而在数组中,我们已知道的数组名代表数组首元素的地址,而&符号是取出某个元素的地址,那我们看一段代码来了解一下&数组名和数组名的区别:

在这里插入图片描述
当我们打印&数组名的地址和数组名的地址时,发现他们的结果是一样的,那是不是代表二者等价呢

我们已经知道指针±整数的意义,当我们对上面的代码进行±整数时:
在这里插入图片描述

当我们对两者进行+1的操作时,结果有了偏差,这就明白了&a和a的值虽然一样,但是所表示的意义并不同,从他们+1所差的字节数,我们可以大致总结一下

&arr其实表示的是整个数组的地址,并不是数组首元素的地址,这样数组地址+1,相差40,也就是跳过了整个数组的地址
arr代表数组首元素的地址,+1自然跳过一个元素(int4个字节),打印出来的地址是跳过的下一个元素的地址

数组名是数组首元素的地址我们已经知道了,但是这种情况也有两个例外

1.sizeof(数组名):数组名单独放在sizeof()内部时,这里的数组名表示整个数组,计算的是整个数组的大小
2.&数组名,这里的数组名也表示整个数组,这里取出的是整个数组的地址

数组传参和指针传参

当在一个函数中需要运用到数组时,就需要把“数组”传递给函数,在数组传参过程中,会有很多种情况

首先看一下一维数组传参,下面几段代码是一维数组传参的实例

void test(int a[])//将数组作为参数存过去,函数形参拿数组来接收,因为形参本身不创造数组,所以可以省略数组大小
{}
void test(int a[10])//与第一种情况相同,数组大小标明10
{}
void test(int *a)//传参过来的是arr是数组首元素地址,地址拿指针接收
{}
void test2(int *a[20])//虽然a2是指针数组,但也是数组,形参也用指针数组接收
{}
void test2(int **a)//实参传过去一级指针的地址(a2),用二级指针来接收
{}
int main()
{
 int a[10] = {0};
 int *a2[20] = {0};
 test(a);
 test2(a2);
}

最后一个例子可能有些特殊,这里画图作解释:
在这里插入图片描述

既然有数组传参,那有一维数组就一定会有二维数组,我们看几个二维数组传参的实例:

void test(int a[3][5])//二维数组传参,二维数组接收
{}
void test(int a[][])//这个传参形式是错误的,形参部分根据数组的知识可知行可以省略但列不能省略
{}
void test(int a[][5])//二维数组传参可以省略行有几个元素
{}

void test(int *a)//这个传参形式是错误的,数组名是首元素地址,是一行的地址,不是一个整型的地址所以不能传给一级指针
{}
void test(int* a[5])//这个传参形式是错误的,传过去的是数组名,是地址,但是形参是指针数组,所以不行
{}
void test(int (*a)[5])//形参是数组指针,可以指向一行,每个元素是整型
{}
void test(int **a)//这个传参形式是错误的,实参传过去是地址,是第一行的地址不可以用二级指针接收。
{}
int main()
{
 int a[3][5] = {0};
 test(a);
}

ps:二级指针是用来接收一级指针的地址的

在这里插入图片描述----->通过一级指针与二级指针传参的几个实例,我们大概也了解了传参的规则,那总结以上,当一个函数部分为一级指针的时候,函数能接收什么参数呢?当函数的参数为二级指针的时候,又可以接收什么参数?

对于函数参数为一级指针来说,可以传一个变量的地址,一个指向变量的指针,数组名....总而言之,只要传过去的类型与形参类型匹配,就可以
对于函数参数为二级指针时,可以传一个一级指针的地址,一个二级指针,指针数组,
函数指针

函数指针就是指向函数的指针变量

我们已经知道,数组名就是数组当地址,那么函数名是不是函数的地址呢?我们尝试打印函数名,发现二者结果一样,这说明函数名也作函数的地址
在这里插入图片描述
接下来看两个特殊的代码

1(*(void (* )( ))0)();

在这里插入图片描述

2void (*signal(int , void(*)(int)))(int);

在这里插入图片描述

以上两段代码均摘自《C陷阱与缺陷》

通过分析我们发现这两段代码理解起来很复杂,对于这种情况,我们就可以利用typedef来对指针重命名,让这段代码看起来更容易理解

typedef int* p     将指针重新命名为p
typedef int(*p)[10]            重新对数组指针类型重命名为p
typedef int(*p)(int,int)      定义一个函数指针变量

综上可知,上面第2个代码例子就可以利用typedef转化成:

typedef void(*pf_t)(int)
   pf_t signal(int,pf_t);

这样所表示的含义就一清二楚了

函数指针数组

通过上面的介绍,我们了解了整型指针数组字符指针数组,那既然数组是可以存放一组相同的元素,指针可以指向函数,必然就可以有函数指针数组。

函数指针数组就是数组的每个元素是函数指针类型:

//例如在我们自定义了一系列Fun函数之后,就可以将他们设置为函数指针数组
int (*p[4])[intint]= {Fun1,Fun2,Fun3....... }

既然可以有函数指针数组,那就随之也可以有指向函数指针数组的指针等等等,一层一层地“嵌套”。

看到这里本章就结束了伟大的程序员辛苦了在这里插入图片描述

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值