int   *(*a)[3][4]  


  求    

  sizeof(a)  

  sizeof(*a)  

  sizeof(**a)  

  sizeof(***a)  

  sizeof(****a)  

  结果我知道,有没有人能解释下具体原因。


其中,  

  a         是一个指向2维指针数组的指针。  

  *a       是2维指针数组。  

  **a     是一维指针数组。  

  ***a   是数组中的元素,也就是一个指向整数的指针。  

  ****a   是一个整数。  


  这样,每一个的大小就清楚了。对于32位平台,4,48,16,4,4


注意这个指针占4个字节,int也占4个字节。  

  要是把定义改成   char   *(*a)[3][4],对应大小就变成   4,48,16,4,1了。指针占的大小没变,char只占1个字节。  

这个问题涉及到“结合性”。[]运算符的结合性大于*,另外,变量定义要从右往左读,所以  

  int**   a[3][4]应该解释为:有一个3*4的数组,它的名字叫a,每个元素的类型是int**。  

  不信你可以试试这个程序:  

  #include   <iostream>  

  #include   <typeinfo>  


  using   namespace   std;  


  int   main()  

  {  

  int**   a[3][4];  

  int**   p;  

  if(typeid(a[0][0])==typeid(p))  

  cout<<"a[0][0]   is   an   int**"<<endl;  

  }  


  那么int*   (*a)[3][4]怎么解释呢?仍然是从右往左:  

  有一个3*4的数组,它的名字是(*a),它的每一个元素都是int*  

  也就是说,(*a)是这个数组,那么a呢,就是这个数组的第一个元素[0][0]的地址。  

  因此,a的大小是4  

  而(*a)由于是3*4的数组,所以大小是12*4=48

接下来,用*运算符解引用,同样,解引用也要按照优先级去解。由于[]运算符优先级比*高,所以解第二层的时候等于说是解掉了[3],所以我们得到的是int*   something[4],它的大小当然是16。再解一层,是去解那个[4],于是就成了int*   something,当然是4了。再解一层就是int   something了,所以是int的大小。  


  这就是为什么lann64(昆仑大鹏@迦楼罗)   说,把int换成char之后,最后一行结果是1的缘故。

​javascript:void(0)​



///////////////////////////

很多大师都说过这个左右法则,这里有点献丑。写这篇文章也主要是想让大家指出我的错误,提高我自己,同时和大家分享我的收获。


首先将“左右法则”的原文搬出来吧:

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.


上面其实已经说得很清楚了,但是刚看这一段时,我还是有很多迷糊的地方。下面我用中文(呵呵,也只能用中文,E文太烂)来讲一讲我的理解。


从最里面开始,也就是从你的变量开始一层一层往外推。最里面那个就是主体,每外面一层都是修饰紧挨着的里面一层的。

所以,你从内向外每看完一层以后,就可以把你看完的都当作一个整体,然后紧挨着的外面这一层就是来修饰这个整体的。

下面的例子中,我上面提到的这个“整体”依次用X1,X2,X3。。。来表示,以便你能看得更清楚,不被迷惑到了。


举例说明吧:


int* a;      


int (*a)();    


int *(a[10]);   


int (*a)[10];   


int (*((*a)[10]))(int)


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


呵呵,晕了吧?我觉得把握住左右法则的要点,按我的理解就是上面提到的:

“从最里面开始,也就是从你的变量开始一层一层往外推。最里面那个就是主体,每外面一层都是修饰紧挨着的里面一层的。

所以,你从内向外每看完一层以后,就可以把你看完的都当作一个整体,然后紧挨着的外面这一层就是来修饰这个整体的。”

百变不离其宗,无论有多复杂,用左右法则层层拨茧抽丝,就可以化繁为简。


上面我的例子中用括号比较多,有些括号还是“多余”的,因为我实在懒得去记C中繁多的优先级与结合性,用括号表示一目了然,也便于别人阅读我的代码。呵呵,我也顺便提醒一下大家,记清这些优先级不错,但是出来卖弄就不对了:)


函数指针我在大学的时候只是听说过,当时不屑一顾,认为这没什么作用,直接调用函数不就可以了?何必那么麻烦?。但是工作以后发现,函数指针实在太有用了。比如说,如果你想在C中秀一秀你的面向对象的技术,那么你可以在用struct来代替class,在struct中用函数指针来代替成员函数。当然函数指针还有许多的用处,呵呵,我才疏学浅,希望以后能深入学习一下写一点这方面的体会。网上有很多资料,很快可以搜索到,你也不必等我的那篇还不知道会不会写的文章到白头:)


好了,咱们继续,现在我们把顺序倒一倒,看看如何用左右法则来根据自己的需要写出确切的申明。


1,一个指向int型数据的指针。

这个so easy: int* a;

但是我要说明一下步骤,因为下面的繁的例子和这个的原理是一样的。

首先抓住要点:这是一个指针,于是我们可以写下 *a。

然后这个指针时指向int型数据,也就是int X1。

然后用 *a替换X1,就变成了 int (*a);

这里因为比较简单,可以去掉括号,变成 int* a;



2,一个有十个指向没有参数返回值为int型的函数的指针的元素的数组。

首先,这是一个数组,有十个元素,我们可以写下:a[10];

去掉对数组的描述,上面的这段话就变成:

“指向没有参数返回值为int型的函数的指针”

我们只抓最外一层:指针。于是变成了:*(a[10])。

去掉对指针的描述,这段话就变成:

“没有参数返回值为int型的函数”

呵呵,这个函数就是 int x();

我们把x用上面的*(a[10])来替代,就变成了:

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

呵呵,大功告成。