复合类型的混合使用--右左法则和替换规则

复合类型的混合使用

c++的基本复合类型主要有三种:数组,引用和指针。

由于语法上的自由性,导致这三者嵌套的情况下变得十分复杂,虽然我们可以用更加简便,易于阅读的方案替换一些复杂声明,但是我们还是有必要掌握复杂程序的阅读能力。

举个栗子:

                                 int* ( *( *func )( int* ) )[10];

是不是觉得很无厘头,放心,今天我们就来细细说说如何理解和使用复合类型的混合使用。

这里是一位大神的理解:http://blog.csdn.net/code_crash/article/details/4854965

右左法则

原文如下:

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.

直译如下:

右左法则是这样的:从最里面的括号开始阅读,然后从右向左读。当遇到括号时,我们就掉转阅读方向,一旦括号里的所有东西解析完毕,跳出括号。一直这样重复直到表达式解析完毕。

我们可以这样理解:
所谓的最里面的括号,指的是包含变量名的最小括号。
然后我们要记住四种运算符的优先级:

  • () > [] > * > &

这样我们就基本掌握了阅读顺序,可惜,仅仅是掌握阅读顺序是不够的,笔者这里有一种思路,我们暂且叫它替换规则,有助于我们理解嵌套表达式。

替换规则

我们在理解了阅读顺序之后,从最里面的括号开始,按照运算符优先级,逐步替换复杂表达式为单一表达式,最终转化为单一复合类型的形式。
注意,我们使用变量名来替换内层表达式时最好符合一定规则,具体见例子。

实例

笔者的描述可能有一定偏差,我们直接来看例子,先从简单的开始:

实例1
int *(&array)[10] = ptrs;

我们按照之前的规则,逐步地来:

  • 1、找到变量名array,这是一个引用,说明array的顶层是一个引用,这时我们将(&array)替换为ref,表达式变为如下形式:
int *ref[10] = ptrs;
  • 2、此时按照优先级,我们看到 [10] ,这一层是一个数组,我们将 ref[10]替换为ref_arr:
int *ref_arr = ptrs;
  • 3、现在按照替换规则,已经到了单一的复合类型,不需要再替换了,不过为了方便理解,我们还是记录一下变量的最终形态:

ref_arr_ptr_int

我们按照从左向右解析这个变量名:

array是一个引用,引用的对象是数组,数组的元素是指针,指针指向的对象为int
也就是存放十个int指针的数组的引用(好拗口)

实例2

我们继续复杂点的例子:

int(*func)(int *p, int(*f)(int*));
  • 1、首先判断变量名,这里有多个变量名:func,p和f。当遇到这种情况的时候,表明这里有一个函数,除了变量名以为都是参数名,一般来说,最左边的变量名是函数的变量名,也是我们找的变量名。此处为func,func是一个指针.
int ptr(int *p, int(*f)(int*));
  • 2、ptr右边是一个括号,表明ptr是一个函数,函数的参数我们要单独理解,但是我们这样记录:
int prt_fun(int *p, int(*f)(int*));
  • 3、第一个参数是一个ptr_int,第二个参数按照我们之前的例子,最后的结果是这样:ptr_fun(ptr_int)
int prt_fun(ptr_int, ptr_fun(ptr_int));

解析如下:

func是一个函数指针,返回值为int,该类型函数的参数为两个,一个是int指针,另一个是一个函数指针,参数为int指针( :) 更加拗口了)

实例3

这下我们再来看文章开头的例子

int* ( *( *func )( int* ) )[10];
  • 1、找到变量名func
int* ( *ptr( int* ) )[10];
  • 2、右边一个括号,ok,是个函数,我们省略参数判断
int* ( *ptr_fun( ptr_int ) )[10];
  • 3、又是个指针,当函数整体需要替换时,我们省略参数列表,当然最后解析的时候需要把它加上
int* ( ptr_fun_ptr )[10];

这里我们怎么理解呢,这里特别地,函数的下一个对象我们理解为函数的返回值,这里说明返回值是一个指针

  • 4、我们继续,根据右左规则和优先级,右边是一个数组
int* ptr_fun_ptr_arr;
  • 5、最后变成这样:ptr_fun_ptr_arr_ptr_int

重头戏,来理解这个表达式:

func是一个函数指针,函数的参数是int指针,返回值是一个指针,指针指向数组,数组内部存了10个int指针

规则总结

这里补充总结一点解析规则:
1、按照从左往右的顺序解析
2、遇到fun表明是函数,如果fun后面不是(),则表明后面是返回值,省略了参数列表,参数和返回值都需要隔断开单独理解。

有了这两个规则,基本上能理解所有的表达式了。

使用举例–函数返回二维数组

我们知道,由于数组是不可拷贝的,所以返回值不允许是数组,但是我们可以返回其首地址指针来得到二维数组
这里是一个很好的例子(不是笔者写的,笔者能理解,但是并不会写这样奇怪复杂的函数)
当然,如果你熟练掌握了c++的内存结构,也可以强制的通过指针操作得到二维数组的所有元素

int ( *fun( int( & )[3][3] ) )[3];
......
int main( void )
{
    .....
    int a[3][3] = ..........;
    int ( *p )[3] = fun( a );
    .....
    p[1][2] = 3;    //等效于a[1][2] = 3
    .....
    return 0;
}

int ( *fun( int( &a )[3][3] ) )[3]
{
    return a;
}

用typedef和functional进行简化

虽然有所谓的规则便于阅读理解复杂表达式,但是我们在实际使用当中倾向于使用更加方便的代码风格,例如使用typedef

实例3可以这样表达:

typedef  int*(*RET)[5];
typedef RET(*func)(int *);

其实typedef就是一种编译器层面的替换规则。

当然,如果使用到const要注意typedef的对象永远是顶层的cosnt,而非字符串替换,笔者在上一篇博客中讲const时曾有过介绍。

在面向对象的程序中,使用函数指针需要使用functional头文件的方法,关于std::bind和std::function,本篇不做多介绍,记住在类中不能简单地使用函数指针,我们有机会再详细地介绍这一块的内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值