肯尼斯·里科《C和指针》第13章 高级指针话题(1)进一步探讨指向指针的指针变量的高级声明

13.1 进一步探讨指向指针的指针

上一章使用了指向指针的指针,用于简化向单链表插入新值的函数。另外还存在许多领域,指向指针的指针可以在其中发挥重要的作用。这里有一个通用的例子:

这些声明在内存中创建了下列变量。如果它们是自动变量,则无法猜测它们的初始值。

有了上面这些信息之后,请问下面各条语句的效果是什么呢?

①如果ppi是个自动变量,它就未被初始化,这条语句将打印一个随机值。如果它是个静态变量,这条语句将打印0。

②这条语句将把存储ppi的地址作为十进制整数打印出来。这个值并不是很有用。

③这条语句的结果是不可预测的。对ppi不应该执行间接访问操作,因为它尚未被初始化。

接下来的两条语句用处比较大。

ppi = π

这条语句把ppi初始化为指向变量pi。以后就可以安全地对ppi执行间接访问操作了。

*ppi = &i;

这条语句把pi(通过ppi间接访问)初始化为指向变量i。执行完上面最后两条语句之后,这些变量变成了下面这个样子:

现在,下面各条语句具有相同的效果:

i='a';
*pi='a';
**ppi='a';

在一条简单的对i赋值的语句就可以完成任务的情况下,为什么还要使用更为复杂的涉及间接访问的方法呢?这是因为简单赋值并不总是可行,例如链表的插入。在那些函数中,无法使用简单赋值,因为变量名在函数的作用域内部是未知的。函数所拥有的只是一个指向需要修改的内存位置的指针,所以要对该指针进行间接访问操作以访问需要修改的变量。

在前一个例子中,变量i是一个整数,pi是一个指向整型的指针。但ppi是一个指向pi的指针,所以它是一个指向整型的指针的指针。假定我们需要另一个变量,它需要指向ppi。那么,它的类型当然是“指向整型的指针的指针的指针”,而且它应该像下面这样声明:

int  ***pppi;

间接访问的层次越多,需要用到它的次数就越少。但是,一旦真正理解了间接访问,无论出现多少层间接访问,我们应该都能十分轻松地应付。

只有当确实需要时,才应该使用多层间接访问。不然的话,程序将会变得更庞大、更缓慢并且更难于维护。

批注:上面的描述看懂并不困难,但是需要结合具体例子来理解吧。

13.2 高级声明

在使用更高级的指针类型之前,我们必须观察它们是如何声明的。前面的章节介绍了表达式声明的思路以及C语言的变量如何通过推论进行声明。我们在第8章声明指向数组的指针时已经看到过一些推论声明的例子。现在通过观察一系列越来越复杂的声明进一步探索这个话题。

首先来看几个简单的例子:

int        f;      /* 一个整型变量 */
int        *f;    /* 一个指向整型的指针 */

不过,请回忆一下第2个声明是如何工作的:它把表达式*f声明为一个整数。根据这个事实,肯定能推断出f是个指向整型的指针。C声明的这种解释方法可以通过下面的声明得到验证:

int*    f, g;

它并没有声明两个指针。尽管它们之间存在空白,但星号是作用于f的,只有f才是一个指针。g只是一个普通的整型变量。

下面是另外一个例子,以前曾见过:

int        f();

它把f声明为一个函数,它的返回值是一个整数。旧式风格的声明对函数的参数并未提供任何信息。它只声明f的返回值类型。现在将使用这种旧式风格,这样例子看上去简单一些,后面再回到完整的原型形式。

下面是一个新例子:

int        *f();

要想推断出它的含义,必须确定表达式*f( )是如何进行求值的。首先执行的是函数调用操作符(),因为它的优先级高于间接访问操作符。因此,f是一个函数,它的返回值类型是一个指向整型的指针

接下来的一个声明更为有趣:

int    (*f)();

确定括号的含义是分析这个声明的一个重要步骤。这个声明有两对括号,每对的含义各不相同。第2对括号是函数调用操作符,但第1对括号只起到聚组的作用。它迫使间接访问在函数调用之前进行,使f成为一个函数指针,它所指向的函数返回一个整型值

函数指针?是的,程序中的每个函数都位于内存中的某个位置,所以存在指向那个位置的指针是完全可能的。函数指针的初始化和使用将在本章后面详述。

现在,下面这个声明应该是比较容易弄懂了:

int        *(*f)();

它和前一个声明基本相同,f也是一个函数指针,只是所指向的函数的返回值是一个整型指针,必须对其进行间接访问操作才能得到一个整型值。

现在,让我们把数组也考虑进去:

int        f[];

这个声明表示f是个整型数组。数组的长度暂时省略,因为我们现在关心的是它的类型,而不是它的长度。

下面这个声明又如何呢?

int        *f[];

这个声明又出现了两个操作符。下标的优先级更高,所以f是一个数组,它的元素类型是指向整型的指针

下面这个例子隐藏着一个圈套。不管怎样,让我们先推断出它的含义。

int        f()[];

f是一个函数,它的返回值是一个整型数组。这里的圈套在于这个声明是非法的——函数只能返回标量值,不能返回数组

这里还有一个例子,颇费思量。

int        f[]();

现在,f似乎是一个数组,它的元素类型是返回值为整型的函数。这个声明也是非法的,因为数组元素必须具有相同的长度,但不同的函数显然可能具有不同的长度。

但是,下面这个声明是合法的:

int        (*f[])();

首先,必须找到所有的操作符,然后按照正确的次序执行它们。同样,这里有两对括号,它们分别具有不同的含义。括号内的表达式*f[ ]首先进行求值,所以f是一个元素为某种类型的指针的数组。表达式末尾的( )是函数调用操作符,所以f肯定是一个数组,数组元素的类型是函数指针,它所指向的函数的返回值是一个整型值。

如果大家搞清楚了上面最后一个声明,下面这个应该是比较容易的了:

它和上面那个声明的唯一区别就是多了一个间接访问操作符,所以这个声明创建了一个指针数组,指针所指向的类型是返回值为整型指针的函数。

到现在为止,这里使用的是旧式风格的声明,目的是为了让例子简单一些。但ANSI C要求我们使用完整的函数原型,使声明更为明确。例如:

int        (*f)( int, float );
int        *(*g[])( int, float );

前者把f声明为一个函数指针,它所指的函数接受两个参数,分别是一个整型值和浮点型值,并返回一个整型值。后者把g声明为一个数组,数组的元素类型是一个函数指针,它所指向的函数接受两个参数,分别是一个整型值和浮点型值,并返回一个整型指针。尽管原型增加了声明的复杂度,但我们还是应该大力提倡这种风格,因为它向编译器提供了一些额外的信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/461740.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

问题:必须坚持以中国式现代化推进中华民族伟大复兴,既不走封闭僵化的老路,也不走 #媒体#知识分享

问题:必须坚持以中国式现代化推进中华民族伟大复兴,既不走封闭僵化的老路,也不走 A、中国特色社会主义道路 B、改革开放之路 C、改旗易帜的邪路 D、中国式现代化之路 参考答案如图所示

Linux 36.2@Jetson Orin Nano基础环境构建

Linux 36.2Jetson Orin Nano基础环境构建 1. 源由2. 步骤2.1 安装NVIDIA Jetson Linux 36.2系统2.2 必备软件安装2.3 基本远程环境2.3.1 远程ssh登录2.3.2 samba局域网2.3.3 VNC远程登录 2.4 开发环境安装 3. 总结 1. 源由 现在流行什么,也跟风来么一个一篇。当然&…

RabbitMQ的延迟队列实现[死信队列](笔记一)

关于死信队列的使用场景不再强调,只针对服务端配置 注意: 本文只针对实现死信队列的rabbitMQ基本配置步骤进行阐述和实现 目录 1、docker-compose 安装rabbitMq2、查看对应的版本及插件下载3、安装插件和检测 1、docker-compose 安装rabbitMq a、使用d…

IT行业有哪些证书含金量高呢?

目录 引言: 一、 计算机网络类证书 二、 数据库管理类证书 三、 安全与信息技术管理类证书 四、 编程与开发类证书 五、 数据科学与人工智能类证书 六、结论: 悟已往之不谏,知来者犹可追 …

备战蓝桥杯---动态规划(基础3)

本专题主要介绍在求序列的经典问题上dp的应用。 我们上次用前缀和来解决,这次让我们用dp解决把 我们参考不下降子序列的思路,可以令f[i]为以i结尾的最大字段和,易得: f[i]max(a[i],a[i]f[i-1]); 下面是AC代码: #in…

猫头虎分享已解决Bug || JavaScript语法错误(Syntax Error):SyntaxError: Unexpected token

博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …

机器学习:分类决策树(Python)

一、各种熵的计算 entropy_utils.py import numpy as np # 数值计算 import math # 标量数据的计算class EntropyUtils:"""决策树中各种熵的计算,包括信息熵、信息增益、信息增益率、基尼指数。统一要求:按照信息增益最大、信息增益率…

多线程JUC:线程池原理、自定义线程池详细解析

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:多线程&JUC:等待唤醒机制(生产者消费者模式) 📚订阅专栏:多线程&…

L1-095 分寝室

一、题目 二、解题思路 遍历所有情况&#xff0c;i 为女生寝室数量&#xff0c;n-i 为男生寝室数量&#xff0c;循环的结束条件为不允许单人住一间寝室 所有待分配的学生都必须分到一间寝室&#xff1a;i<n0 && n-i<n1 && n-i>0 &#xff1b;对每种…

Java汽车销售管理

技术架构&#xff1a; springboot mybatis Mysql5.7 vue2 npm node 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、财务报表等功能&#xff0c;提供经理和销售两种角色进行管理 效果图&…

Ansible自动化工具(1)

目录 ansible的特性&#xff1a;. 二.部署ansible 管理端安装 ansible&#xff1a; ansible 目录结构&#xff1a; 管理主机上配置主机清单&#xff1a; ​编辑 配置密钥对验证&#xff1a; ansible 命令行模块 &#xff1a; 1&#xff0e;command 模块 指定 ip 执行…

数据结构 - 线索树

一、 为什么要用到线索二叉树&#xff1f; 我们先来看看普通的二叉树有什么缺点。下面是一个普通二叉树&#xff08;链式存储方式&#xff09;&#xff1a; 乍一看&#xff0c;会不会有一种违和感&#xff1f;整个结构一共有 7 个结点&#xff0c;总共 14 个指针域&#xff0c…