如何读懂复杂的C语言声明
本文已迁移至:
http://www.danfengcao.info/c/c++/2014/02/25/howto-understand-complicated-declaration-of-c.html
C语言中有时会出现复杂的声明,比如
char * const * (*next) (); //这是个什么东东?
在讲复杂声明的分析方法前,先来个补充点。
C语言变量的声明始终贯彻两点:
1.声明和使用的语法尽量保持一致
例如:
#include <iostream>
using namespace std; double (*fun)(double); //声明一个函数指针 int main(){
fun=sin;
doube reslut=(*fun)(0.5);//使用这个函数指针
...
}
2.声明语句不是的阅读不是按照从左往右的阅读顺序,而是要根据各个符号的优先级进行阅读的
这点非常重要!先列出C语言声明的优先级规则,再举个例子就能掌握求解方法了。
C语言声明的优先级规则
A 声明从它的名字开始读取,然后按照优先级顺序依次读取;
B 优先级从高到低依次是:
B.1 声明中被括号括起来的那部分;
B.2 后缀操作符:
括号()表示这是一个函数,而
放括号[]表示这是一个数组;
B.3 前缀操作符:星号*表示这是一个“指向...的指针”;
C 如果const和(或)volatile关键字的后面紧跟类型说明符(如int,long等),那么它作用于类型说明符。在其他情况下,它作用于关键字左边紧邻的指针星号。
需要强调的一个重要注意点是,对于优先级: () > [] > * 。
举例,用优先级规则分析C语言声明一例:
char * const * (*next) ();
按照规则解读 char * const * (*next) ();
首先,(*next) 表示next是一个指针,它指向某个东东;
根据B.2和最右边的括号,next指向一个函数;
根据B.3,第二个星号表示,该函数返回一个指针;
char * const是函数返回的指针所指向的类型;
char* 是字符指针,const修饰左边的星号,即字符指针是常量的,该指针不可修改(该指针指向的字符内容是可修改的);
综合地表述为,next是一个指针,它指向一个函数,该函数返回一个指针,该指针指向一个类型为char的常量指针。
还是不明白?再往下看就明白了!
#---------------------------------------------------------------------------------#
总结一下,分析复杂的C语言声明,要采用"由内而外,层层剥离”的策略。
从哪里开始剥?从语句的最左边的标志符开始剥(上例为从next开始)。
往哪个方向剥?依照C语言的优先级规则一层层剥。
#---------------------------------------------------------------------------------#
再举一例作详细说明:
char *(* c[]) (int **p);
第一步,char *(* c[10]) (int **p); 最左边的标志符是c,表示"c是一个什么东东";
第二步,char *(* [10]) (int **p); 和[10]结合,表示"c是一个长度为10的数组";
第三步,char *(* ) (int **p); 和*结合,表示"这个数组存放着指针";
第四步,char * (int **p); 和(int **p)结合,表示"这个指针指向一个函数,函数的参数是二维指针";
第五步,char * ; 和 * 结合,表示"这个函数返回一个指针";
第六步,char ; 这个指针指向一个字符;
把上面六步串起来,读作: c是一个数组[0..9],它存放着指针,指针指向的函数参数是一个二维指针返回值是指向字符的指针。完工!
#---------------------------------------------------------------------------------#
注1: 这个方法若理解了,什么指针数组和数组指针、指针函数和函数指针等等之类的区别都是小菜一碟了。
注2: 合法的声明中存在限制条件。如函数的返回值不能是一个函数,也不能是一个数组,所以像function()()和function()[]是非法的,不能出现。数组里面能存函数指针,但不能存函数,像int (* function[])()是合法的,function[]()则是非法的。
#---------------------------------------------------------------------------------#
参考文献
《C专家编程》, Peter Van Der Linden 著, 徐波 译
如何读懂复杂的C语言声明的更多相关文章
- 一篇文章读懂JSON
什么是json? W3C JSON定义修改版: JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻量级的文本数据交换格式,并不是 ...
- 一文读懂Redis常见对象类型的底层数据结构
Redis是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis支持五种常见对象类型:字符串(String).哈希(Hash).列表(List).集合(Set)以及有序集合( ...
- 一文读懂Java动态代理
作者 :潘潘 日期 :2020-11-22 事实上,对于很多Java编程人员来说,可能只需要达到从入门到上手的编程水准,就能很好的完成大部分研发工作.除非自己强主动获取,或者工作倒逼你学习,否则我们好 ...
- C#基础之IL ,轻松读懂中间代码IL 转载
[No0000152]C#基础之IL,轻松读懂IL 先说说学IL有什么用,有人可能觉得这玩意平常写代码又用不上,学了有个卵用.到底有没有卵用呢,暂且也不说什么学了可以看看一些语法糖的实现,或对.n ...
- Java8 函数式【1】:一文读懂逆变
Java8 函数式[1]:一文读懂逆变 禁止转载 pure function 协变 逆变 Java8 引入了函数式接口,从此方法传参可以传递函数了,有人说: 不就是传一个方法吗,语法糖! lambda ...
- 从Spring中学到的【1】--读懂继承链
最近看了一些 Spring 源码,发现源码分析的文章很多,而底层思想分析的文章比较少,这个系列文章准备总结一下Spring中给我的启示,包括设计模式思想.SOLID设计原则等,涉及一些编程的基本原则, ...
- Gradle学习系列之三——读懂Gradle语法
在本系列的上篇文章中,我们讲到了创建Task的多种方法,在本篇文章中,我们将学习如何读懂Gradle. 请通过以下方式下载本系列文章的Github示例代码: git clone https://git ...
- 如何快速读懂大型C++程序代码
要搞清楚别人的代码,首先,你要了解代码涉及的领域知识,这是最重要的,不懂领域知识,只看代码本身,不可能搞的明白.其次,你得找各种文档:需求文档(要做什么),设计文档(怎么做的),先搞清楚你即将要阅读是 ...
- 如何解析复杂的C语言声明
C语言中有时会出现复杂的声明,比如 char * const * (*next) (); //这是个什么东东? 在讲复杂声明的分析方法前,先来个补充点. C语言变量的声明始终贯彻两点 : ...
随机推荐
- SQL SERVER四舍五入你除了用ROUND还有其他方法吗?
引言 今天和测试沟通一个百分比计算方式时遇到一个问题, 我在存储过程里用到了强转CAST(32.678 AS DECIMAL(5,1)) 我认为该方式只会保留一位小数,我给测试的回复是我并没有用到四 ...
- knockoutjs+ jquery pagination+asp.net web Api 实现无刷新列表页
Knockoutjs 是一个微软前雇员开发的前端MVVM JS框架, 具体信息参考官网 http://knockoutjs.com/ Web API数据准备: 偷个懒数据结构和数据copy自官网实例 ...
- jquery的事件命名空间详解
jquery现在的事件API:on,off,trigger支持带命名空间的事件,当事件有了命名空间,就可以有效地管理同一事件的不同监听器,在定义组件的时候,能够避免同一元素应用到不同组件时,同一事件类 ...
- [Asp.net 5] Logging-日志系统的基本架构(下)
接上节内容,我们继续讲解日志的其他部分. ILoggerProvider以及扩展类 我们在上节的架构图上并没有看到有直接实现该接口的实现类.那么如果将Logger类直接使用会有什么结果呢? var f ...
- C#使用Graphics画圆写字
画填充圆: Graphics gra = this.pictureBox1.CreateGraphics(); gra.SmoothingMode = System.Drawing.Drawing2D ...
- 错误:下列软件包有未满足的依赖关系: openssh-server : 依赖: openssh-client (= 1:7.1p1-4)
解决办法:尝试了很久才解决,这个是我发现最有用的,完美的解决了我的困难 后续过程参考:http://blog.csdn.net/jszhangyili/article/details/8881807 ...
- 关于java中接口定义常量和类定义常量的区别
/** * * @author YZJ * @Description java中定义常量的最佳方法 */ public final class Contants{ /** * @Description ...
- linux lin命令
ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s,具体用法是:ln –s 源文件 目标文件. 当我们需要在不同的目录,用到相同的 ...
- link与@import的区别
我们都知道link与@import都可以引入css样式表,那么这两种的区别是什么呢?先说说它们各自的链接方式,然后说说它们的区别~~~ link链入的方式: <link rel="st ...
- 【百度文库课程】Java语言基础与OOP入门学习笔记一
一. Java的历史与由来 原名Oak,针对嵌入式系统开发设计,语法与C/C++基本一致 二. Java语言特点 Java由四方面组成:Java编程语言.Java类文件格式.Java虚拟机和Java应 ...