指针数组,数组指针,指针函数,函数指针(转)

int   *p[4];         //指针数组。  是个有4个元素的数组, 每个元素的是指向整型的指针。(数组的每个元素都是指针)
int   (*p)[4];       //数组指针。 它是一个指针,指向有4个整型元素的数组。                (一个指针指向有4个整型元素的数组)
int *func(void);     //指针函数。 无参函数, 返回整型指针。             (函数的返回值为int*)    
int (*func)(void);   //表示函数指针,可以指向无参, 且返回值为整型指针的函数。      (函数的返回值为int)

右左规则:

因为C语言所有复杂的指针声明,都是由各种声明嵌套构成的。如何解读复杂指针声明呢?右左法则是一个既着名又常用的方法。不过,右左法
则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。C标准的声明规则,是用来解决如何创建声明的,而右左法则是用
来解决如何辩识一个声明的,两者可以说是相反的。右左法则的英文原文是这样说的:
The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you
encounter parentheses, the direction should be reversed. Once everything in the parentheses has been
parsed, jump out of it. Continue till the whole declaration has been parsed.
这段英文的翻译如下:
右左法则:首先从最里面的圆括号(未定义的标识符)看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明

解析完毕。
笔者要对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识符,但未定义的标识符只会有一个。
现在通过一些例子来讨论右左法则的应用,先从最简单的开始,逐步加深:
int (*func)(int *p);
首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针,然后跳出这个圆括号,先看
右边,也是一个圆括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有int*类型的形参,返回值类型是 int。

int (*func)(int *p, int (*f)(int*));
func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int *和int (*)(int*)这样的形参,返回值为

int类型。再来看一看func的形参int (*f)(int*),类似前面的解释,f也是一个函数指针,指向的函数具有int*类型的形参,返回值为int。

int (*func[5])(int *p);
func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰 func的,而是修饰func[5]的,原因是[]运算符优先级

比*高,func先跟[]结合,因此*修饰的是func[5]。跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为

int。

int (*(*func)[5])(int *p);
func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是

指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下,就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,

返回值为int类型的函数。

int (*(*func)(int *p))[5];
func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。
要注意有些复杂指针声明是非法的,例如:

int func(void) [5];
func是一个返回值为具有5个int元素的数组的函数。但C语言的函数返回值不能为数组,这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言

的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回值不能为数组。

int func[5](void);
func是一个具有5个元素的数组,这个数组的元素都是函数。这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,

即使函数的类型一样,但函数所占用的空间通常是不相同的。

 

实际当中,需要声明一个复杂指针时,如果把整个声明写成上面所示的形式,对程序可读性是一大损害。应该用typedef来对声明逐层

分解,增强可读性,例如对于声明:

int (*(*func)(int *p))[5];
可以这样分解:
typedef  int (*PARA)[5];
typedef PARA (*func)(int *);
这样就容易看得多了。
答案,同时给出用typedef的分解方法:
int (*(*func)[5][6])[7][8];
func是一个指向数组的指针,这类数组的元素是一个具有5X6个int元素的二维数组,而这个二维数组的元素又是一个二维数组。
typedef int (*PARA)[7][8];
typedef PARA (*func)[5][6];
int (*(*(*func)(int *))[5])(int *);
func是一个函数指针,这类函数的返回值是一个指向数组的指针,所指向数组的元素也是函数指针,指向的函数具有int*形参,返回值为int。
typedef int (*PARA1)(int*);
typedef PARA1 (*PARA2)[5];
typedef PARA2 (*func)(int*);
int (*(*func[7][8][9])(int*))[5];
func是一个数组,这个数组的元素是函数指针,这类函数具有int*的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素
的数组。
typedef int (*PARA1)[5];
typedef PARA1 (*PARA2)(int*);
typedef PARA2 func[7][8][9];

 

原帖链接:http://www.cnblogs.com/zhangjing0502/archive/2012/06/08/2542059.html

转载于:https://www.cnblogs.com/chenzhefan/p/4372008.html

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

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

相关文章

8086除法指令DIV,IDIV

无符号除法指令DIV(DIVision) DIV OPRD ;除数OPRD决定是8位除法还是16位除法;OPRD8位,则被除数默认在AX中,AX除以OPRD的商保存在AL中,余数保存在AH中;OPRD16位,则被除数默认在DX与AX中,结果的商保存在AX中,余数保存到DX中assume cs:code data segmentdb 2,4 data ends code se…

Hibernate 基础配置及常用功能(二)

本章主要是描述几种经典映射关系,顺带比较Hibernate4.x和Hibernate5.x之间的区别。 一、建立测试工程目录 有关实体类之间的相互映射关系,Hibernate官方文档其实描述的非常详细,这里只提供几种常见映射。(推荐4.3.11版本的 hibern…

三言两语

人生中总是在选择。每当做一件事我们都应该问问我们的内心,或多或少我们都能理解一点人生的真谛。 最近时间很充裕,也就想了好多事情。首先我想明白的第一件事就是做任何事就要勇敢的去面对、去追求。喜欢一个女孩子大概有8年了吧!这期间我们…

8086逻辑移位指令SHL和SHR

SHL逻辑左移指令 SHL OPRD M;把操作数OPRD左移M位,M为位移次数,为1或为CL(位移超过1次用CL表示) ;每移动一位右边用0补足一位,移出的最高位进入CF(最后移出的一位写入CF) MOV AL,00010011B ;13H 00010011B SHL AL,1 ;把AL左移1位,移出的最高位0进入CF,右边0补足1位…

YTU 2903: A--A Repeating Characters

2903: A--A Repeating Characters 时间限制: 1 Sec 内存限制: 128 MB提交: 50 解决: 30题目描述 For this problem,you will write a program that takes a string of characters,S,and creates a new string of characters,T,with each character repeated R times.That is,…

JavaScript 模拟装饰者模式

/*** 抽象coffee父类,其实可以不用的*/ function Coffee () {} Coffee.prototype.cost function() {throw 实现这个方法; }; /*** 黑咖啡,其实可以不用继承的;*/ function BlackCoffee () {} // BlackCoffee.prototype new Coffee(); // Bl…

8086算术移位指令SAL和SAR

SAL算术左移指令同逻辑左移指令进行相同动作,机器指令一样,只是为了方便记忆而提供的两个助记符 SAR算术右移指令 SAR OPRD,M ;该指令使操作数右移M位,每移动1位左边的符号保持不变,移出的最低位进入CF mov al,26H ;00100110B 右移1位 00010011B sar al,1 ;26H/2H13H mov a…

const 和readonly

原文:http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 关于 const和readonly修饰符之间的区别,要牵涉到C#中两种不同的常量类型: 静态常量(compile-time constants) 和动态常量(runtime constants) 静态常量是指编译器在编译时候会对常量进行解析,并将常量的…

Objective - C 小谈:UIPickerView 和 UIDatePicker的基本使用

1.UIPickerView 1.1. UIPickerView的常见属性 // 数据源(用来告诉UIPickerView有多少列多少行) property(nonatomic,assign) id<UIPickerViewDataSource> dataSource;// 代理(用来告诉UIPickerView每1列的每1行显示什么内容,监听UIPickerView的选择) property(nonatomic,…

8086移位指令

8086有如下3条一般移位指令 SAR OPRD,M ;算术右移 对无符号数和有符号数而言右移1位相当于原数除以2 SHR OPRD,M ;逻辑右移 对无符号数右移1位相当于原数除以2 SHL OPRD,M/SAL OPRD,M ;逻辑/算术左移(两个助记符只有一个机器指令,进行相同的动作)左移1位相当于原数*2

ADO连接ACCESS数据库

首先在StdAfx.h中加入 建立连接&#xff1a;(在xxApp文件中) 1 声明变量 2 建立连接 (1) AfxOleInit 初始化 OLE 为应用程序的支持。 BOOL AFXAPI AfxOleInit( ); 返回值 非零&#xff0c;如果成功;0&#xff0c;如果初始化失败&#xff0c;可能&#xff0c;因为安装该 OLE 系…

MySQLdb autocommit的坑

今天写的一个小功能&#xff0c;里面要用MySQLdb更新数据库&#xff0c;语句如下 sql "update %s.account_operation set status1 where username%s" % (allResDBInfos[db], username)变量替换后&#xff0c;是下面的样子 update suspects.account_operation set st…

8086段寄存器

8086有四个段寄存器CS,DS,SS,ES 任意时刻CPU执行CS:IP指向的指令,CS为代码段寄存器(IP为指令指针寄存器) 任意时刻SS:SP指向栈的栈顶单元,SS为栈段寄存器 我们寻找数据需要知道数据在内存的位置用DS寻址 DS为数据段寄存器 ES为附加段寄存器可作为目的地址的段地址比如ES:DI…

用jquery给元素绑定事件,一些内部细节

按看段代码&#xff1a; 1 $(.test).on(click, function() { 2 console.log(hello); 3 $(this).removeClass(test); 4 }); 就算是remove掉class test&#xff0c;照样可以点&#xff0c;事件绑定的是这个对象。 转载于:https://www.cnblogs.com/lqj12138/p/4384596.html

8086数据寄存器

8086CPU有四个16位数据寄存器可分成8个8位寄存器 AX(AH,AL)|BX(BH,BL)|CX(CH,CL)|DX(DH,DL) 数据寄存器主要用来保存操作数和保存运算结果等 AX 常用作累加器(accumulator)用来保存临时数据比如MOV AX,DATA将数据段地址送入AX ;MUL BL,DIV BX用来保存乘除法的结果 BX 基(Ba…

使用搜索栏过滤collectionView(按照首字母)

1.解析json数据NSDictionary *citiesDic [CoreJSONSerialization coreJSONSerialization:"cities"];NSDictionary *infor [citiesDic objectForKey:"infor"];NSArray *listItems [infor objectForKey:"listItems"]; 2.存储数据 for (NSDicti…

《哪来的天才?练习中的平凡与伟大》

这是一本堪称论述所有伟大成就来源的书中最让我觉得激动人心、非常棒的一本书。 什么成就了一个那些所谓的天才&#xff1f;刻意练习&#xff01;伟大的成就不是因为所谓天生的基因&#xff0c;也不是所谓简单的埋头苦干。而是需要长时间有针对性的刻意提高自己某个方面能力的艰…

8086变址和指针寄存器

SI和DI称为变址寄存器,在字符串操作中SI作为源指针,DI作为目的指针(ES:DI<--DS:SI) ;用作存储器指针时可用于寻址 DS:[SI],DS:[BXDI]BP和SP称为指针寄存器,BP称为基址针,SP为堆栈指针 ;BP也可作为存储器指针DS:[bpsi],如果没有段前缀那么BP最为堆栈基址[BP]寻址的是堆栈内存…

R软件中 文本分析安装包 Rjava 和 Rwordseg 傻瓜式安装方法四部曲

这两天&#xff0c;由于要做一个文本分析的内容&#xff0c;所以搜索了一天R语言中的可以做文本分析的加载包&#xff0c;但是在安装包的过程&#xff0c;真是被虐千百遍&#xff0c;总是安装不成功。特此专门写一篇博文&#xff0c;把整个心塞史畅快的释放一下。 ------------…

省赛之路第一天

今天是清明假期第一天&#xff0c;原定的到洛阳玩也成为了虚无缥缈的东东了吧&#xff0c;cb这位还有说的太对了&#xff0c;no game&#xff0c;no girlfriend&#xff0c;no holiday&#xff0c;only maching&#xff01;这都不是什么大事&#xff0c;毕竟自认为还是个肯吃苦…