C——优先级下指针声明的细节

本文详细介绍了C语言中指针声明的细节,特别是关于数组和指针结合的情况。通过解析操作符优先级,解释了`char (*p)[10]`与`char *p[10]`的区别,以及如何声明数组中包含函数指针的技巧。文章适合有一定C语言基础的读者深入理解指针声明。
摘要由CSDN通过智能技术生成

前言

 如果你希望更加充分的了解C语言在声明指针时,最终编译器是如何处理的。这篇文章可以帮助你,并且可以让你理解以下代码区声明。
 顺便说一下,以下的内容启发都来自《C和指针》、《C专家编程》。你可以阅读完这篇文章,再去细品一下这两本书!

void (* test(int,void *))()
//你是否能够理解上面的究竟干了什么

PS

 这篇文章并不适合一些C基础不好的人,需要到其他的地方补充一下基础知识。
之所以不添加那么多的内容,旨在能够把标题讲明白而已!

C指针

 从几个大家相对熟悉的指针声明开始。

char *p;//char类型的指针
char *p();//返回char类型指针的函数
char *p[];//一个数组,元素类型为char *

这些都是比较熟悉的指针声明。
现在提高一点难度:
char *p[10]char (*p)[10]的区别在哪里?
前者是这样的:

一个数组,包含10个元素,并且每个元素的类型为char 指针

后者是这样的:

一个指针,指向一个数组,这个数组的元素类型为char,并且包含10个元素

到这里,其实编写过一些C程序的人,可能也知道这个意思。但是,究竟是如何得出这样的结果呢?
 答案都在这个操作符优先级上了。我们看一下(来自《C和指针》第81页,部分删减,需要的自行查找)

操作符描述用法示例结果类型结合性
()聚组(表达式)与表达式同N/A
()函数调用rexp(rexp,rexp)rexpL-R
[]下标引用rexp[rexp]rexpL-R
.访问结构成员repx.memberlexpL-R
->访问结构指针成员rexp->member_namelexpL-R
++后缀自增rexp++rexpL-R
后缀自减rexp–rexpL-R
!逻辑反!rexprexpR-L
++前缀自增++rexprexpR-L
前缀自减–rexprexpR-L
*间接访问*rexprexpR-L

对照表格(优先级从高到低),我们开始吧。
先从char *p[]开始吧

  1. 找到声明的标识符,在这个例子上,我们找到了p
  2. 我们对比一下p左右两边的操作符,此时发现[]*,对比优先级,发现[]明显高于*,因此此时的p标识符会先和[]结合,那么此时p就会表示为数组
  3. 接着p会和*结合,所以这个数组的元素为指针类型

char (*p)[]

  1. 找到声明的标识符,我们依旧找到p
  2. 我们对比发现,p的附近已经被()包围,编译器会理解为聚组(最高的优先级),会强制修改p*先进行结合,这样一来,p就是一个指针
  3. 接着p会和[]结合,这意味这p指针将指向一个数组,并且类型为char

这就能够说明这两者之间的区别是如何而来的

复杂的指针

 那么接下来,我们讨论高级点的东西,如果你需要用指针指向函数呢?(函数指针)

/*比如一个返回int,无参数的函数foo
*/
int foo();
指针该如何指向他呢?

int *p(),这个显然并不能指向foo函数,因为p标识符肯定会和优先级高的()结合形成一个函数。那这注定不能是一个指针。所以我们得修改这种优先级。
修改的方式就是上一个例子的(),结果就是:int (*p)()。现在你肯定已经为什么要这样声明函数指针了。

数组中的元素为函数指针

 简单说明一下这个小标题的意思:

我希望一个数组的元素里可以记录一下函数,并且通过下标调用这些函数,返回int值。我该怎么办呢?

尝试一下:

  • 首先我们得让标识符先和[]结合,让其成为一个数组,即p[]
  • 我们得让这个数组的元素为函数,因此得紧着(),即p[]()

着答案看似已经出来了,int p[](),但是很不幸编译器会报错,因为编译器不允许数组包含函数。那怎么办?

在《C和指针》的261页中这样描述的:数组元素必须具有相同的长度,因此函数很显然不符合这样的条件。但在《C专家编程》59页简单的总结了C语言下的声明。其中包括了数组元素不能是函数

在《C专家编程》59页中的声明总结中,数组元素可以为函数指针,因此我们可以通过这种方式用数组去存储函数(感觉说法不太对劲)。
回到前面的p[](),我们的p[]后面的需要一个指针所以很简单的得出答案:*p[],接着这个指针是要指向一个函数的。但()高于*。所以我们还是得强制利用()聚组去修改默认的优先级,所以答案就很明显了:

int (*p[])() = &foo
/*这里需要注意,函数名总是由编译器把它转换为函数指针,因此&只是显示表示这种情况*/

回到最开始的void (* test(int,void *))()
结果为:

test是一个函数test(int,void *),这个函数需要两个参数:一个是int型的数值,一个是void *指针,它将返回一个指针* test(int,void *),这个指针是一个函数(即函数指针)(* test(int,void *))(),这个函数指针是一个无参数,无返回值的函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值