版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
概述
' z$ x4 g6 d R# g
# d3 Y7 \% j1 r; S' R 在很多情况下,尤其是读别人所写代码的时候,对C语言声明的理解能力变得非常重要,而C语言本身的凝练简约也使得C语言的声明常常会令人感到非常困惑,因此,在这里我用一篇的内容来集中阐述一下这个问题。& v% m# p: t" l. M, [# i
5 }) p. H- g8 o' B2 i$ p3 F
问题:声明与函数, z+ v$ _& ]+ c ?2 |/ e2 ^+ x* B, q
- m& Q8 ]. f, ^
有一段程序存储在起始地址为0的一段内存上,如果我们想要调用这段程序,请问该如何去做?
8 C: X% _; s* |' |$ G& U# ^9 Y/ R6 ?2 Q9 J
答案& P5 C! |6 }. j- W
5 M- W- a) d* Y7 |1 B 答案是(*(void (*)( ) )0)( )。看起来确实令人头大,那好,让我们知难而上,从两个不同的途径来详细分析这个问题。, m' m- `; b) F% f
G7 ~- x6 ^5 _ j; { Q7 r 答案分析:从尾到头
9 u% F( L, w! B. g
, N# ?7 k- @! J7 m" R) p 首先,最基本的函数声明:void function (paramList);
0 W) z2 r7 {1 w" C4 T6 s" t
) N1 Q7 { ~5 v# w1 n$ F5 \7 H 最基本的函数调用:function(paramList);
$ V4 O$ Y1 L! H M* w6 w2 K; c T
8 m& {4 U& A: O- l+ ~' q7 K 鉴于问题中的函数没有参数,函数调用可简化为 function();
# b n, P* _" F
' L7 M3 n* q* d- x; F7 q. z X 其次,根据问题描述,可以知道0是这个函数的入口地址,也就是说,0是一个函数的指针。使用函数指针的函数声明形式是:void (*pFunction)(),相应的调用形式是: (*pFunction)(),则问题中的函数调用可以写作:(*0)( )。
7 ?% ]/ f+ P; b+ D+ t
$ _! t( P: P5 W6 } 第三,大家知道,函数指针变量不能是一个常数,因此上式中的0必须要被转化为函数指针。. B5 h3 y1 f. b1 R4 ^6 R) V
% k% \5 J. j* _! A0 |- D# V 我们先来研究一下,对于使用函数指针的函数:比如void (*pFunction)( ),函数指针变量的原型是什么?这个问题很简单,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,清晰起见,整个加上()号。, a$ z) D+ z8 O1 ^
% J: B* ^2 c# G 所以将0强制转换为一个返回值为void,参数为空的函数指针如下:( void (*)( ) )。) Z. V# u6 ?/ f& o: x- L1 U
3 S" ]% \5 }, C U+ l5 @, k
OK,结合2)和3)的分析,结果出来了,那就是:(*(void (*)( ) )0)( ) 。
- {5 F& j" a7 v7 n2 C$ M
8 v' N% e0 r$ ]$ n9 F 答案分析:从头到尾理解答案
; b) `' d. j5 h! x$ L; m
4 m) j O; T8 J+ o1 r( X (void (*)( )) ,是一个返回值为void,参数为空的函数指针原型。! {( |6 y) \, i
(void (*)( ))0,把0转变成一个返回值为void,参数为空的函数指针,指针指向的地址为0.* ]* {. b( A _, x# v8 Q
*(void (*)( ))0,前面加上*表示整个是一个返回值为void的函数的名字: g6 j; I+ {2 E4 I4 m3 A& b
(*(void (*)( ))0)( ),这当然就是一个函数了。. C$ Y2 B3 M* D, f
* X+ o8 M) J6 u; H F0 Q9 O
我们可以使用typedef清晰声明如下:. R* R8 c" J! n) X* R2 o7 t% d
8 _0 `' s/ [& P+ h# F" N4 G$ B typedef void (*pFun)( );
/ B' U. q* j, v
, L' G8 j9 R+ ~ 这样函数变为 (*(pFun)0 )( );4 M5 [$ O% ^8 ^- S! G) a" R- V
1 b k6 s; r" \6 V! j5 [! t) C# A
问题:三个声明的分析
% U8 j, _+ e y s* J
1 q, k7 Z7 ?) }6 x 对声明进行分析,最根本的方法还是类比替换法,从那些最基本的声明上进行类比,简化,从而进行理解,下面通过分析三个例子,来具体阐述如何使用这种方法。: k' t/ N( ] W* v& Q2 J' {1 `
0 d0 }8 I7 m: O$ E% S* e
#1:int* (*a[5])(int, char*);
/ n- z ?# B3 T% V! l% ?6 }' ]! s$ A9 H- U
首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向“(int, char*)”,很明显,指向的是一个函数,这个函数参数是“int, char*”,返回值是“int*”。OK,结束了一个。:)9 d$ x4 \; Z# p; I) A- F
& U' B4 \: ~( B# K( B2 v#2:void (*b[10]) (void (*)());- z' i) g/ ^0 U h8 R
! R9 b, ^8 }; M9 E0 ?
b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注10】,返回值是“void”。完毕!5 ?3 S$ l: s) @' D# ? ]7 E
, ^- |8 \! H/ W4 N s 注意:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。
8 h0 n7 k% N4 y4 y( [0 E$ ~$ ]; C
) J6 r; Q& u0 J5 b( W0 q) W#3. doube(*)() (*pa)[9];
7 K8 R* c% ^8 b8 q& u: p7 L/ i, o4 m! n& V" m1 O; [7 ^: X! h
pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”(也即一个函数指针,指向一个函数,这个函数的参数为空,返回值是“double”)。# M- Q& d% Y. |' u5 K
typedef用法小结- -' o z4 O1 y1 i4 x
在C语言的情况下,与C++稍有出入。
9 R1 b) ~6 O" @ 这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:. K8 d' T; d: ^8 P
来源一:Using typedef to Curb Miscreant Code9 k; n) ~- a3 t
Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。& U# j5 [5 M8 h" E7 k
typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。: h* v/ c6 x* c J
如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?
l; A* s: ] e7 n 使用 typedefs 为现有类型创建同义字。
0 M/ Z! v0 ?: H+ l3 H4 L 定义易于记忆的类型名
5 D; N2 H5 E4 s$ `& _* S# I typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:
$ ]" b8 b, y6 F% _6 r: s typedef int size;/ O1 k9 \4 z) J
此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:
U4 h3 _" J/ m6 M, p& | void measure(size * psz);! U3 K- X: I( @+ g% v
size array[4];
$ s m- p5 I. {- X3 Q1 V; f4 ` size len = file.getlength();
: b( L( r& a3 \; {0 K std::vector vs;
% U2 Q; w7 X( b- ] typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:9 e7 [+ d% E! I+ ^. @! X
char line[81];2 Y$ t' @7 n6 M
char text[81];
- p8 I2 R' }" m+ ~5 u 定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:7 n2 q3 C' O' W0 ~: L' }
typedef char Line[81];% b0 Z, ^, m: x1 }# h
Line text, secondline;0 U2 r; Z. e& f, Y: L' o" _7 P6 R
getline(text);. N* ?3 M- ^/ M5 @
同样,可以象下面这样隐藏指针语法:% v3 \2 `# {: R$ o) E0 X; ?* W
typedef char * pstr;/ h1 c0 @* n# A$ [5 n M. O7 t, l
int mystrcmp(pstr, pstr);
$ l/ {. B% D2 g 这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():0 [+ O- m0 m0 H# A8 T
int mystrcmp(const pstr, const pstr);
6 C6 G0 H0 J3 d& G/ Z+ l# u% G: s 这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:8 s8 w7 ]$ m, J2 I5 Z
typedef const char * cpstr;( e& Y- z1 C! A9 [5 S, @$ b
int mystrcmp(cpstr, cpstr); // 现在是正确的
3 l' J% R. x* U1 W0 z* Q7 V 记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。2 {$ `' n4 I- f7 p4 @; E
代码简化/ ?4 P1 D7 i7 E; Y0 x
上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:
, _# _8 k, B _* X H& A0 n: v: f2 Z) ] typedef int (*PF) (const char *, const char *);) ]# M! {8 h& g/ a- T! S
这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:. \" h# L6 K6 B: Y5 h
PF Register(PF pf);
* L- k3 R) Y- B7 t: b# W9 g Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
Y9 w7 j9 c& ], S5 s. @: y/ ` | int (*Register (int (*pf)(const char *, const char *)))
9 K% z- |) O. }2 ~3 X \: f+ o4 A (const char *, const char *);
6 O( e @. c+ l5 | 很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件,一个有同样接口的函数。
" M+ a% O& R/ h0 I G5 @9 M- u3 A typedef 和存储类关键字(storage class specifier)
8 ~: c0 ^5 ~, |/ f0 |* m% U 这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:
8 ^: [+ K* Z7 t2 q typedef register int FAST_COUNTER; // 错误; {2 A% j- S2 M% Z$ ~- z
编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。
" }' d \; I6 D# D; u3 [1 z% ` 促进跨平台开发
5 s" D# _, {) x9 S typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:# d) V4 u5 d2 b9 {$ ~' P
typedef long double REAL;; x% r& S. K$ c8 a5 O R( S8 h
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
. @8 R6 H, a" b/ `" l1 m1 b5 X typedef double REAL;
) m( e3 s0 q% Y) U$ @& P0 {9 J 并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:、
# Y1 G9 X; p6 H! m. a typedef float REAL;
9 a/ m9 g3 M. z8 ?7 o/ r& H; w: V 你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。# n" I5 X; j! h8 O
作者简介
5 `) }% Z3 A8 I7 A3 d) F Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。# B; ?( r, O, p( i! C; t' O
来源二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)& F9 S% s6 F% c
C语言中typedef用法
+ x+ U% _( |+ n8 p( B 1. 基本解释: N: X& m$ r' }# t+ [/ `4 q: U
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
+ ^0 u1 ?5 w; P0 s 在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。0 r, ]( [0 x( {
至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。
4 F( @- |: O8 O7 k2 _ 2. typedef & 结构的问题: D! ^$ a; q- }. }) |8 F I3 u
当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?莫非C语言不允许在结构中包含指向它自己的指针吗?请你先猜想一下,然后看下文说明:2 M% Q5 a% X1 v
typedef struct tagNode x6 v1 T* `9 A1 Z0 e* W
{6 S K" K1 z+ u2 j% H
char *pItem;! U9 R% [5 K: b' `
pNode pNext;
5 h& Z, m) k+ c" r3 A1 x } *pNode;
. k2 i0 u. Q* I; l2 g& y 答案与分析:3 g/ N n* ~, ]- q; h
1、typedef的最简单使用
6 _) F/ C) [: X* b0 L# C; d typedef long byte_4;
3 ~6 Z" w& r1 a9 Z: L( j 给已知数据类型long起个新名字,叫byte_4。
( j: i" e9 W$ \1 ]: }/ B 2、 typedef与结构结合使用2 f$ o5 C$ P. M" Q' n6 x
typedef struct tagMyStruct9 a4 ]' o7 V9 g! {& l
{
5 Z6 P- P `. V int iNum;
% L' l" b' n# x& t) u, D5 s long lLength;
8 T+ t. x, z( m! L } MyStruct;
F+ X" @- P! e+ `2 @ 这语句实际上完成两个操作:5 H! [2 c& y" h- X' g- [
1) 定义一个新的结构类型
* u' |5 O. Z- W struct tagMyStruct/ \8 V. c; s+ s
{3 v9 E' B# P7 R% Q8 ]" u
int iNum;7 ^7 S. _9 I0 V( \" a$ F5 B
long lLength;
, v% ~9 \/ c* X) K- x };
) m; }/ o& R2 K( Q! D' R 分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。7 R, h& Y2 v( k/ f
我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。, x% v- i+ s7 D9 [# F
2) typedef为这个新的结构起了一个名字,叫MyStruct。; [: f+ P" V" M G. T- }' _+ c
typedef struct tagMyStruct MyStruct;
5 s& ?8 T8 l! q: e7 q 因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
3 U$ h; C+ _2 }6 j 答案与分析! l" Y5 M$ z- `+ t8 p
C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。* t9 B, {2 ?4 U7 c
根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。( Z4 D6 w$ A1 C0 Q+ N& k9 ~
解决这个问题的方法有多种:
, P9 i, }3 N, K! Z; l' z: J. c 1)、$ h" S4 O/ U8 G d7 t8 p, i: m
typedef struct tagNode! z! B1 I4 ]" A0 ? U$ B5 J* h- h
{3 ~4 r; v) A9 O; e6 R& N) `
char *pItem;
# c* A: b0 r* f* A/ T0 J struct tagNode *pNext;3 _9 K5 m9 v: S: |0 H
} *pNode; y# M% T% s% q6 K+ K" i9 f" {) C( M
2)、
3 g+ K4 {' a" {* D' x- {) ]8 ^; Z typedef struct tagNode *pNode;! d; ~% X3 u* M
struct tagNode
& @" T9 `( |, w5 r2 v {
7 k, E+ ?) w, P \ char *pItem;# }) t# p! Z3 }7 x5 M+ N
pNode pNext;
2 ?% F; t9 U- s( f0 ~1 d: I b7 B };" I- o; G4 N8 r. {1 m8 B9 u
注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。$ U \& M2 F- {
3)、规范做法:" M# Z5 J7 E3 t/ W$ A
struct tagNode
1 ]( h4 _- Q$ r! D' V8 j" | {
r1 F- ]7 Z9 v2 a( P% Y char *pItem;* ^; ^7 F& @; z4 F
struct tagNode *pNext;0 l' G) ]3 |( I
};
3 R) R$ t' c o. i% ^8 a typedef struct tagNode *pNode;) c W$ A. U3 d' U! \; o' {+ z
3. typedef & #define的问题
: P. h4 r- N; } 有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?
, P/ E) T$ ]3 J typedef char *pStr;" u) w9 X3 g+ x. P1 a' j- | ~
#define pStr char *;
! y1 h; K4 O' A/ n9 Z" w 答案与分析:
; y/ q& ]) x0 s$ T5 X$ A 通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:8 h& ~% y6 \/ O6 ^7 C5 i
typedef char *pStr1;
* X7 C$ I9 {# T0 v; _3 c* R H' k #define pStr2 char *;
+ H0 r' C2 o! {. U4 t; e' o! a pStr1 s1, s2;2 B c) q4 s/ r$ r1 o; v
pStr2 s3, s4;
7 m& P1 F3 }: n5 m 在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。: y5 n4 l% U* V) I
#define用法例子:
7 [" x5 T. i% ], ^0 B #define f(x) x*x
5 q0 _+ ~$ R+ t- c5 k main( )# Y* m: O- D2 `2 P9 O
{7 M" U# O/ J. Y- ?1 P" }8 y1 Y
int a=6,b=2,c;
9 K/ q6 g C, i+ [ c=f(a) / f(b);
0 L3 ^) k3 ^& Q4 T7 _( g6 ]5 I. G printf("%d \\n",c);
3 }" V4 o& S- d$ b" N# I4 a# Q2 C }* r, f* P! c( m/ m; ~) @
以下程序的输出结果是: 36。/ ], [* P( N# a5 s. C
因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:* r. z8 B# K* M6 g
#define f(x) (x*x)
7 {" w9 G: G, q, K B: t 当然,如果你使用typedef就没有这样的问题。+ v8 R9 K, \( ] p: P
4. typedef & #define的另一例* V2 I! m3 X. N% H3 D0 i
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
" C* H2 n9 f& O3 | typedef char * pStr;9 C9 Z# |' L. b% \2 n
char string[4] = "abc";
+ |9 H ^% ~9 h/ a) X; k6 c& h const char *p1 = string;3 y5 t( P; s9 \- m: C& H
const pStr p2 = string;
: w/ q, }! \. ]4 u& \1 [ p1++;
- O, i, e- @5 d: c p2++;: l+ m, h2 H- c$ C( y" ~* ]$ l
答案与分析:
, v0 X D$ A- b 是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。. C4 n# B( q) l3 Q/ t; @$ x% E
#define与typedef引申谈
2 `7 m% S& M1 k1 g# B+ I: q( A 1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。) h! w! y* a: _( p8 V% T$ p
2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。! k. S" j' |+ v$ }" E( V7 H1 S, F
5. typedef & 复杂的变量声明, y& @+ A. s8 @0 f
在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:
% @6 j) h: p: _$ y 下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
$ q$ Z: B, I6 J, {; H >1:int *(*a[5])(int, char*);; \* L0 K8 ^) c1 w4 V" I
>2:void (*b[10]) (void (*)());
- ^' p4 |. \8 c" e >3. doube(*)() (*pa)[9];0 R# w) q0 v& k- A& ^+ \
答案与分析:3 _' e/ H9 _3 C T
对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。$ X: Y2 J" d/ V9 ?/ P9 \) Y
>1:int *(*a[5])(int, char*);
0 r# f# q M" ]0 e' W5 a' F //pFun是我们建的一个类型别名/ |" ^' ~0 B# p) e
typedef int *(*pFun)(int, char*);
, ]& t& f) l8 w( U: p8 ^ | //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
4 U0 ~, A/ p4 C) F" n- z pFun a[5];$ Z, ]; t! H/ F' [( E! o2 q" T" w0 f
>2:void (*b[10]) (void (*)());* h0 g3 N9 |1 I
//首先为上面表达式蓝色部分声明一个新类型# T3 ?, l8 T W& o9 R
typedef void (*pFunParam)();8 y7 P8 W {% [2 g4 B9 e: f
//整体声明一个新类型8 a+ `" ^5 c* L: n- W2 {1 {$ K6 e J
typedef void (*pFun)(pFunParam);! |+ O( I5 e( e/ |9 Z
//使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());. K7 d# F9 F: x4 N
pFun b[10];6 s. v! t6 f' ~1 g' I, k) w9 [
>3. doube(*)() (*pa)[9];+ ^$ P% @# V7 }
//首先为上面表达式蓝色部分声明一个新类型
8 [6 x7 s1 {, B2 ] typedef double(*pFun)();
! k' e' ^- b: s //整体声明一个新类型7 Z+ j( r2 Y# N: M/ W
typedef pFun (*pFunParam)[9];
$ k \7 Z5 C4 _; s9 H //使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9];4 k3 |* N( O+ I: t( _
pFunParam pa; |
|