版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
概述
7 s9 h2 H/ }2 w* \" D9 O' T+ f% {: {; Z' W
在很多情况下,尤其是读别人所写代码的时候,对C语言声明的理解能力变得非常重要,而C语言本身的凝练简约也使得C语言的声明常常会令人感到非常困惑,因此,在这里我用一篇的内容来集中阐述一下这个问题。( r5 p$ w, ^4 n5 W
! D$ \8 M& q6 |0 E
问题:声明与函数
' C8 F4 ]+ \0 c! j, D
. O# x% `% U, O4 F# z/ n 有一段程序存储在起始地址为0的一段内存上,如果我们想要调用这段程序,请问该如何去做?, n0 L/ x4 z' G7 l& @6 j& m7 J+ m
0 X, S# j0 s/ f; l 答案
" |4 D4 s, t' J$ o3 c+ G# |& f( O& r
答案是(*(void (*)( ) )0)( )。看起来确实令人头大,那好,让我们知难而上,从两个不同的途径来详细分析这个问题。
5 j3 S+ C9 {/ U9 x, h8 i# S+ v( e: _, h9 J# R. \3 r% i
答案分析:从尾到头
1 d* P6 w1 D1 z1 u A+ O5 `9 O, J! I( {+ D) X. S
首先,最基本的函数声明:void function (paramList);
- x) R0 F" L; V* V( s
+ I1 w6 c& k/ {0 r; A7 O: v# n X 最基本的函数调用:function(paramList);/ t" E- T" y) q, C% [8 R' Z! Z
; ?# s. K3 s( q# m" B 鉴于问题中的函数没有参数,函数调用可简化为 function();' `$ l: ~4 Y4 N0 z/ x. B) V- c% [; L
5 {4 d1 L6 d8 P$ k& ]/ i O 其次,根据问题描述,可以知道0是这个函数的入口地址,也就是说,0是一个函数的指针。使用函数指针的函数声明形式是:void (*pFunction)(),相应的调用形式是: (*pFunction)(),则问题中的函数调用可以写作:(*0)( )。
. r7 Q# M+ ]& a9 t0 b. D5 ]8 L4 \3 i9 O3 t% _2 v* S& a
第三,大家知道,函数指针变量不能是一个常数,因此上式中的0必须要被转化为函数指针。- Z; X4 y/ @. X% K1 T. p; T
w% k9 [: e7 I. ~. [ 我们先来研究一下,对于使用函数指针的函数:比如void (*pFunction)( ),函数指针变量的原型是什么?这个问题很简单,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,清晰起见,整个加上()号。
4 e6 d! S% }3 E. F0 ^
* X7 r/ e5 P7 h( h. v1 k: Y 所以将0强制转换为一个返回值为void,参数为空的函数指针如下:( void (*)( ) )。& N9 ~$ F1 H2 f: H9 a5 M3 q
# [, p5 ^9 z4 @; s4 G OK,结合2)和3)的分析,结果出来了,那就是:(*(void (*)( ) )0)( ) 。7 |3 E+ R1 I, e- J1 ?
2 J& K. R) v8 G0 q; f4 A Z
答案分析:从头到尾理解答案
7 z9 K( P, k% m; W& L* J$ Y, B7 T- U
(void (*)( )) ,是一个返回值为void,参数为空的函数指针原型。1 a$ l/ s+ e4 Z4 p9 C |
(void (*)( ))0,把0转变成一个返回值为void,参数为空的函数指针,指针指向的地址为0.
2 b# o6 X5 O& S; o2 n *(void (*)( ))0,前面加上*表示整个是一个返回值为void的函数的名字/ o/ g7 x5 V& r4 c" b0 g& I+ C
(*(void (*)( ))0)( ),这当然就是一个函数了。, P. l! V0 d" v6 I8 i) R
# h2 B0 ~) {; e4 G- h+ y3 a 我们可以使用typedef清晰声明如下:9 `/ g) j1 f1 N% O4 S
: }3 ^. ~" y& Y" G' ~) Y typedef void (*pFun)( );9 }; F) I! ^! Q0 _/ r
3 [# @- K9 j* T 这样函数变为 (*(pFun)0 )( );
, t% n7 `* Z2 l& X
. _: h1 S4 T) M" R 问题:三个声明的分析* }9 ]) F. U. O3 y7 _( n
3 o$ G _$ ~. U2 d& u1 I 对声明进行分析,最根本的方法还是类比替换法,从那些最基本的声明上进行类比,简化,从而进行理解,下面通过分析三个例子,来具体阐述如何使用这种方法。% ?3 X! {( T0 F& `( e, i# c# l
: B* ?. x$ h: W4 r& y
#1:int* (*a[5])(int, char*);
z1 j& B3 H! k/ i+ i8 u3 G4 P! v2 O: \; V( x
首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向“(int, char*)”,很明显,指向的是一个函数,这个函数参数是“int, char*”,返回值是“int*”。OK,结束了一个。:)- n- m% u5 y9 F4 ` i; C
! v. D1 w) f; h! S4 c3 B6 n" S#2:void (*b[10]) (void (*)()); K, r P* H6 t! i2 h. u; F5 v. V
3 N( n# O- c! q5 Y b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注10】,返回值是“void”。完毕!8 h. z5 l' a4 C$ N! Y. v
1 T6 E$ C( B8 E3 P& M1 p 注意:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。
" {( l+ p: P3 @7 b$ O$ K9 @5 k/ K6 H$ R4 b+ I5 } @- g/ N
#3. doube(*)() (*pa)[9];* ~2 N! P& \, M, b
, t; Q% t+ Y0 X# r% K6 C
pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”(也即一个函数指针,指向一个函数,这个函数的参数为空,返回值是“double”)。' @; D2 C; E h* c! B$ e1 L
typedef用法小结- -
+ u1 K. o6 ^7 @ 在C语言的情况下,与C++稍有出入。
( x9 ?4 d) x A, @4 Q# n% t% v! V 这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:' ~! f. W4 v4 P! Q
来源一:Using typedef to Curb Miscreant Code( r b$ G5 P# K$ B6 b# v3 h: }" k( |
Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。
( m* P" O4 C. ] typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。/ v; w' i& |4 b3 ` }. U
如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?7 k/ R8 k- i w
使用 typedefs 为现有类型创建同义字。4 g) n L" {/ c' Z* T8 [5 [- I
定义易于记忆的类型名
5 b' |: K8 B8 b9 e typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:9 o( v+ J" W. b6 j4 [$ O
typedef int size;0 ^3 d p# d+ B5 M- G9 d
此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:) C3 l# x. \! k" N) ?4 C: A4 a7 m) Z
void measure(size * psz);
! h2 a! S% Q2 ~+ r size array[4];
( t1 K) n! b4 h3 X9 Q size len = file.getlength();
" Q) d0 W4 i5 A. H( n std::vector vs;
* A2 S' H6 B% R+ Z4 r typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:% l) ~9 Q7 x9 m7 {$ f% R. K
char line[81];$ C6 {) C& d1 I6 ]( |- x
char text[81];
$ E- r* ^/ Y9 @/ V0 M 定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:9 f5 h) _7 p1 v6 p5 C
typedef char Line[81];
7 S7 z. s+ v; h' B Line text, secondline;. x j! s6 e7 l
getline(text);
, Y) S. B" }, s$ t7 f 同样,可以象下面这样隐藏指针语法:( _8 m O; O0 l: z- B0 d* V
typedef char * pstr;
5 I3 G' f5 O; q' u( B) Q8 a int mystrcmp(pstr, pstr);
, O/ S0 M6 P: N9 Z9 w" k/ x: c 这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():
, l+ A: N' P5 [0 k3 d1 {% W int mystrcmp(const pstr, const pstr);
9 O* }+ S. w) p+ l5 b0 o/ s 这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:
; E8 _% }$ y0 a5 h) ?, [ typedef const char * cpstr;
! P! h6 G; s5 U int mystrcmp(cpstr, cpstr); // 现在是正确的
) u1 X7 O9 w- w% O. [ 记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。* a, d3 l8 h. D: \9 j
代码简化
1 L2 {3 h }' \ L. | D; K/ O 上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:
) A( ~6 T9 [& ^3 b+ P. J. I* @ typedef int (*PF) (const char *, const char *);& i! X2 e# |/ X
这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
/ j) L& J. [: d* G: N. ^9 D PF Register(PF pf);
) g5 D* G! T% r! U Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
8 o K5 R+ `0 l* l$ M, e int (*Register (int (*pf)(const char *, const char *)))4 O8 ]9 z1 c3 o0 m# P/ m- G$ D
(const char *, const char *);
, L; U! Q; c6 }# U9 ?; j: \ 很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件,一个有同样接口的函数。/ K! X7 h' A, K* y
typedef 和存储类关键字(storage class specifier)
4 [5 L: a" i3 b 这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:
2 m8 L, r- q' w$ {: `: \ typedef register int FAST_COUNTER; // 错误
! J) p2 D X) D+ V' h" N 编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。6 g5 E3 n/ D# ]& C6 e" w4 o1 D
促进跨平台开发
+ K' X# F, X4 j( X9 x typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度: X& w0 P3 K3 ~0 c2 d& }: r
typedef long double REAL; A8 v0 o# [# J- R# Y: V7 J2 L
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
3 _& O' q6 E4 s6 L. k+ l+ M typedef double REAL;* p e3 \: ]" z: G: V0 |6 l/ j$ p
并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:、 v: i2 s+ D: R% v1 S
typedef float REAL;
4 I8 ?5 x: L" M: y 你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。
# n U4 C/ P* h/ q 作者简介9 Y; S2 ?; z j) \, B) `
Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。6 O/ X! P9 d, s
来源二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)
8 w# }% t2 h y/ P4 E C语言中typedef用法
" p0 k) [, C7 E 1. 基本解释6 l+ u& `! F3 w7 q5 L! m3 u
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
! I4 o3 e& D$ n/ n6 b1 ] 在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。3 p. N5 Y( ~5 n+ i' _
至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。
* [" P9 D* [ {# \ v 2. typedef & 结构的问题
# k% ?! C# M' K' Y+ Z 当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?莫非C语言不允许在结构中包含指向它自己的指针吗?请你先猜想一下,然后看下文说明:2 I* o' T4 D L! z1 t- O& {
typedef struct tagNode. h% {5 I, E/ {. M: v
{) O9 P: h; @4 q6 _; j4 k
char *pItem;( q9 r+ b5 {' t, @! }: p
pNode pNext;- \6 q1 l4 ?& V) G L
} *pNode;& R5 H: v: E3 X: r; R1 u
答案与分析:
T: L2 I: m5 c 1、typedef的最简单使用
* a2 Z' c) c' N1 j; ~ typedef long byte_4;8 M) J& C: B/ |% h: e
给已知数据类型long起个新名字,叫byte_4。
) u% H3 T" C1 p3 v) z% j 2、 typedef与结构结合使用
" p8 ~; k9 L6 X typedef struct tagMyStruct" d# v: j+ g3 x" g% j
{+ Y4 A2 v d) U) E2 `. {
int iNum;
6 s9 e4 N+ K+ m8 G6 [3 Y7 X) w' G long lLength;
# ^6 |# j+ |5 Z: A } MyStruct;. d$ D$ t- y4 e$ L* m
这语句实际上完成两个操作:1 M. Q n3 y+ d* y7 }
1) 定义一个新的结构类型9 _. E( G+ D' _
struct tagMyStruct
+ f A& {. q% s. B" G8 Y C: \ {
9 f; U" F( R; l. m int iNum;
& b0 X y8 Y" |* B' L) H long lLength;
* P) E& G# a; f: G; [ };
* G8 o Y7 p: J6 _" q- I8 w6 V 分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
- F' L U" I: r- B ?. m+ Q2 i 我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
1 }8 s0 ]8 Q1 b ^; d& l$ n 2) typedef为这个新的结构起了一个名字,叫MyStruct。+ `+ D' D1 V' N8 I8 K+ C
typedef struct tagMyStruct MyStruct; h# w% d: \* m
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
# a7 G$ y3 H( w. }# `' F1 e 答案与分析( W- }% x" }" M' v% |" }
C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。% {$ L. m$ z1 C& O
根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。
3 G' J: p, J5 _1 n. F" I 解决这个问题的方法有多种:
% n$ o! i. B3 m) s& @& ` 1)、6 U- J4 {% J9 r6 o
typedef struct tagNode- p9 x9 E4 I3 I( E
{$ C h) b) l4 j- t
char *pItem;
. d- j" F0 G4 { struct tagNode *pNext;
z- ]0 B# k! y* P& K _ } *pNode;
4 w% f, ^+ c1 R( F. ` 2)、4 a7 V2 ~+ K! T& a3 D a
typedef struct tagNode *pNode;2 j7 x/ Y x$ l+ W! D: l- h: N
struct tagNode4 u; X5 I- i( L4 ]! b7 C8 t
{, F @$ ^0 ^/ B4 e( k4 h: t7 H7 w
char *pItem;% l5 S- M/ P% t4 Y
pNode pNext;
+ e. ~3 G! }% g( D7 B0 C7 J! r };+ u0 \+ v- \% P' a) R
注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。9 b3 S0 I- m; u. b) t9 Z6 w! Q
3)、规范做法:# G5 {2 ~1 K+ X5 C7 ~9 s7 F6 k% N* X
struct tagNode7 X: v4 b& a: ~. z) P
{" E7 }; B# @; H `2 M) ]
char *pItem;$ Z" T4 u9 j) _% k. H3 ?# t- z6 t
struct tagNode *pNext;
& _: O# L" S) j" j" D };; n1 w- Q/ J) r% H7 |8 U
typedef struct tagNode *pNode;
# y& O5 o! G5 H, s: x% s 3. typedef & #define的问题6 }3 A* t& m7 l _
有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?
/ R, O) x9 A* J typedef char *pStr;% z3 J2 D4 p. f+ T$ c {+ f
#define pStr char *;
1 ~7 M1 | c2 a: g! w; ^5 U 答案与分析:
2 e" x$ O% e0 n$ ^ 通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
7 q8 B5 N. H9 L) p8 a; c R typedef char *pStr1;, y/ c1 y% I9 [, p2 j
#define pStr2 char *;
9 K7 H+ w5 P4 ?/ J0 j pStr1 s1, s2;& `0 h; H' t8 D: B( n' b3 i; v
pStr2 s3, s4;
$ l1 J9 N( a) t1 c+ o9 W& M 在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。5 q* q# Q8 E9 ~0 [: ?
#define用法例子:
$ G8 i j/ B3 c; J' t7 i- K4 y #define f(x) x*x
) L9 G0 e2 E/ I ` main( )" \8 R8 d4 s6 ?8 S. E+ K4 ~: L
{7 Y4 V1 l- J) F* {& w3 a+ y" K
int a=6,b=2,c;/ R1 u, ?; Q K( d" @/ c. M4 ^. X
c=f(a) / f(b);$ l6 }# _7 ^( t. p
printf("%d \\n",c);
I4 O1 Q- o, T* a }
- W5 y/ I- e: b9 g( ] 以下程序的输出结果是: 36。
9 B4 Q4 m P+ R4 _( ?5 T$ j+ m 因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
) B- E2 R' t8 ?7 d' z2 d #define f(x) (x*x)* B9 ~; v' ?, j" x8 w
当然,如果你使用typedef就没有这样的问题。
7 ?3 F d; a3 K; w 4. typedef & #define的另一例. p4 p( ?! c1 b
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
, B$ b9 N- j E( ]2 Q! ~' e typedef char * pStr;
, k, L- h# ^8 R f' q; a char string[4] = "abc";/ C6 @; ?, E0 }$ }! _9 i% p6 m, K, S
const char *p1 = string;' q, a7 H" H; `0 m& r5 P) g
const pStr p2 = string;1 m/ @% n( {( A
p1++;
+ U! F8 p" ^! g. u& ?! X% ^; d p2++;
7 a O% R: t0 u2 h7 X 答案与分析:2 U# }8 W2 v# q# y
是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。
# ]6 Z* {* |& X% S1 D d9 } #define与typedef引申谈" {8 M# q3 q. i; S7 c
1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。" @' ?( E2 Q* h5 U& d
2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
! A% S( B( g/ J 5. typedef & 复杂的变量声明
# g1 U8 G9 Z6 X$ y" o 在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:
% {& U d. u# ~' f3 k 下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
0 z; j/ L4 P% n l' m/ C' n >1:int *(*a[5])(int, char*); s6 S. ]& } v# w1 j6 F+ X& b
>2:void (*b[10]) (void (*)());
+ G1 c% t; @ N) J6 t; \ >3. doube(*)() (*pa)[9];) s! O, T1 {) L- ^* t- t. D
答案与分析:
1 g n6 d) N. t$ U# a- M7 P 对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。" I6 W. Z* f; D8 E& o9 L2 I* D
>1:int *(*a[5])(int, char*);% J5 ^( k- ^. p, H# L; I0 A1 J+ q W
//pFun是我们建的一个类型别名8 p i% ~, t z8 g1 P0 v
typedef int *(*pFun)(int, char*);
- d6 {: u% @- a //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
% a5 \8 s2 U" C$ X9 ] pFun a[5];
, z, a, U# @ E2 ^/ e, |, \ >2:void (*b[10]) (void (*)());
+ n" I8 W! u6 T7 ^8 ]4 X7 h //首先为上面表达式蓝色部分声明一个新类型
! @6 L. t' o% u" F4 I% Y) E7 v typedef void (*pFunParam)();
|' ]2 t- u5 T( @0 I1 `3 X //整体声明一个新类型
- j4 |; K7 x8 ^ typedef void (*pFun)(pFunParam);
, g5 l# K, C- Z, `5 I( e //使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
* v! l3 ]3 g* {* x2 o) u pFun b[10];
/ P2 s( U4 [3 x7 H$ j7 L8 J >3. doube(*)() (*pa)[9];
0 Y7 B: G t2 t! ]1 T s" U& A //首先为上面表达式蓝色部分声明一个新类型
3 V1 ]# N- L/ N typedef double(*pFun)();5 m9 _+ R/ b" M: [7 {; k& z
//整体声明一个新类型
9 H, F% F$ D q, v4 `6 \ typedef pFun (*pFunParam)[9];% M3 y$ y* I# N$ a& X4 C
//使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9];; g4 J8 M. X3 R$ n4 V g
pFunParam pa; |
|