[转]著名的C语言复杂指针解析过程中右左法则详析
四 4th
因为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个元素的数组,这个数组的元素都是函数。这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的空间通常是不相同的。
作为练习,下面列几个复杂指针声明给读者自己来解析。
int (*(*func)[5][6])[7][8];
int (*(*(*func)(int *))[5])(int *);
int (*(*func[7][8][9])(int*))[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.cnmaizi.com/tech/casm/famous-c-complex-pointer-resolution-right-left-rule
全球(含香港、澳门)旅游订房专业网站-agoda
四 2nd
http://www.agoda.com/ (全球主页)
http://www.agoda.com.cn/ (中文主页)
专业驴友同事介绍的,是全球最大的订房网站,他去泰国就是在这里订的。感觉比国内那些旅游网站好多了,前两天刚订了香港的酒店,各种价位都有(貌似该网站和酒店有合作优惠的,跟酒店当天价格不同),且有不少驴友写的酒店点评,供选择参考。
真诚介绍给旅游新手使用(绝非广告,是就好了。。。)。
全球(含香港、澳门)旅游订房专业网站-agoda
四 2nd
Memcached遍历Key
四 1st
目前功能比较简单,只是遍历所有的key,稍加修改可以 1)取出指定格式(正则等)的key;2)显示value。交互可以更好一点:)
1 # -*- coding:utf-8 -*-
2 import telnetlib
3 import re
4 def send(tn, cmd):
5 tn.write(cmd + "\r\n")
6 return tn.read_until("END\r\n")
7 HOST = "localhost"
8 PORT = 31211
9 tn = telnetlib.Telnet(HOST, PORT)
10 slabs = send(tn, "stats slabs")
11 chunk_pattern = "STAT (?P<type>\d+):chunk_size \d+\r\nSTAT \d+:chunks_per_page \d+\r\nSTAT \d+:total_pages \d+\r\nSTAT \d+:total_chunks (?P<total>\d+)\r\nSTAT \d+:used_chunks \d+\r\nSTAT \d+:free_chunks \d+\r\nSTAT \d+:free_chunks_end \d+\r\n"
12 key_pattern = "ITEM (\S+) \[(\d+) b; (\d+) s\]"
13 ret = re.findall(chunk_pattern, slabs)
14 cnt = 0
15 import datetime
16 for obj in ret:
17 cmd = "stats cachedump %s %s" % (obj[0], obj[1])
18 dump = send(tn, cmd)
19 keys = re.findall(key_pattern, dump)
20 for key in keys:
21 print "%s, %s, %s" % (key[0], key[1], datetime.datetime.fromtimestamp(float(key[2])))
ps: 网上已经有很多类似实现,好吧,我out了。
2011-04-08更新:stats cachedump 输出最多为2MB,超出部分无法得到,这个时间只能显示前边部分key:(。还没看到其他命令可以获取。
// 留意此值为回送内容的最大字节数,当回送内容达到2M时,即使没有达到用户想要的item数也会返回
int memlimit = 2*1024*1024;
……
// !!! alert: 回送内容最多为2M
if (bufcurr + len + 6 > memlimit) /* 6 is END\r\n\0 */
break;
……
9号晚在google上搜索别人的实现,找到一个Xmemcached客户端,在1.2.2版本后支持遍历所有Key。大喜,但快速阅读相关部分代码后发现一样存在这个问题,跟作者确认过了。附上邮件中的部分内容。
原因是:stats cachedump限制了输出的最大字节数为2M,当某一slab class里的item比较多时,该命令会在输出超过2M前返回。除了key的内容以后,cachedump每条信息需要至少5+2+1+4+10+5=27个字节,贪心的情况下key长度应该为3个字节(理论值),总共30个字节,所以最大可显示的key数量不超过69904.9(理论值)个。
Memcached遍历Key
四 1st
目前功能比较简单,只是遍历所有的key,稍加修改可以 1)取出指定格式(正则等)的key;2)显示value。交互可以更好一点:)
1 # -*- coding:utf-8 -*-
2 import telnetlib
3 import re
4 def send(tn, cmd):
5 tn.write(cmd + "\r\n")
6 return tn.read_until("END\r\n")
7 HOST = "localhost"
8 PORT = 31211
9 tn = telnetlib.Telnet(HOST, PORT)
10 slabs = send(tn, "stats slabs")
11 chunk_pattern = "STAT (?P<type>\d+):chunk_size \d+\r\nSTAT \d+:chunks_per_page \d+\r\nSTAT \d+:total_pages \d+\r\nSTAT \d+:total_chunks (?P<total>\d+)\r\nSTAT \d+:used_chunks \d+\r\nSTAT \d+:free_chunks \d+\r\nSTAT \d+:free_chunks_end \d+\r\n"
12 key_pattern = "ITEM (\S+) \[(\d+) b; (\d+) s\]"
13 ret = re.findall(chunk_pattern, slabs)
14 cnt = 0
15 import datetime
16 for obj in ret:
17 cmd = "stats cachedump %s %s" % (obj[0], obj[1])
18 dump = send(tn, cmd)
19 keys = re.findall(key_pattern, dump)
20 for key in keys:
21 print "%s, %s, %s" % (key[0], key[1], datetime.datetime.fromtimestamp(float(key[2])))
ps: 网上已经有很多类似实现,好吧,我out了。
2011-04-08更新:stats cachedump 输出最多为2MB,超出部分无法得到,这个时间只能显示前边部分key:(。还没看到其他命令可以获取。
// 留意此值为回送内容的最大字节数,当回送内容达到2M时,即使没有达到用户想要的item数也会返回 int memlimit = 2*1024*1024; …… // !!! alert: 回送内容最多为2M if (bufcurr + len + 6 > memlimit) /* 6 is END\r\n\0 */ break; ……
9号晚在google上搜索别人的实现,找到一个Xmemcached客户端,在1.2.2版本后支持遍历所有Key。大喜,但快速阅读相关部分代码后发现一样存在这个问题,跟作者确认过了。附上邮件中的部分内容。
原因是:stats cachedump限制了输出的最大字节数为2M,当某一slab class里的item比较多时,该命令会在输出超过2M前返回。除了key的内容以后,cachedump每条信息需要至少5+2+1+4+10+5=27个字节,贪心的情况下key长度应该为3个字节(理论值),总共30个字节,所以最大可显示的key数量不超过69904.9(理论值)个。
2011-03-29
三 29th
到底是要多简单粗暴
三 27th
晚上去看了 <Battle:Los Angeles> 洛杉矶之战
我太失望了,为了能挑个好位子,9点去选了10点05的场
等广告放完,然后我就觉得怎么这么晃呢
我试图通过自身的摇晃来和影片想要制造的混乱场面同步
但是,这完全是徒劳的....
电影开场之后,'艾伦·艾克哈特' 一出来要退役,我就知道这逼肯定是退不了了
并且肯定是要很牛逼的这样那样挽救世界
而且剧情也确实够简单粗暴,’米歇尔·罗德里格兹‘完全是打酱油的啊
最不爽的就是那个外星人,都宇宙飞行到地球了
装备完全不给力,技术水平和我预期的相差太远
虽然不能二向箔攻击,好歹得搞一个水滴过来吧
太低端了,被一个导弹一炸就不行了,你这是坑爹呢
豆瓣6.3分 MTIME7.5分
看来我的风格还是偏向文艺青年啊
最后来张图 外星人的拖拉机攻击,还是老式的蚂蚱腿的:


