版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
概述
! h- q5 c7 m9 d+ N
1 R8 `* g @, Z8 @; J8 r, c, x) ]" p( r 在很多情况下,尤其是读别人所写代码的时候,对C语言声明的理解能力变得非常重要,而C语言本身的凝练简约也使得C语言的声明常常会令人感到非常困惑,因此,在这里我用一篇的内容来集中阐述一下这个问题。
: J, S, B& Z p1 `( W6 |( X {2 @& d$ P, P e
问题:声明与函数
6 {2 U" s# D/ S* d N
. e( x5 e' h, F+ r$ l) T 有一段程序存储在起始地址为0的一段内存上,如果我们想要调用这段程序,请问该如何去做?) c* N6 e9 f+ i' P7 @( l
' t; }" }: H# G 答案$ z, {0 f4 {) o
" C X7 M2 b1 |8 x! z 答案是(*(void (*)( ) )0)( )。看起来确实令人头大,那好,让我们知难而上,从两个不同的途径来详细分析这个问题。9 y6 y, I9 o1 |, X6 M
- J; V3 I" V. d* o
答案分析:从尾到头& E4 C* n3 m- ^2 g U
7 Z" Z) v2 R/ F& o% ?7 m: n5 z 首先,最基本的函数声明:void function (paramList);
! _! Y7 ^) c# y' |" M: W9 I
* T- j5 m& }) e) n; N7 v i0 @ 最基本的函数调用:function(paramList);1 Y: M. {7 p/ O5 m
7 x$ a2 n, \' d4 \. | 鉴于问题中的函数没有参数,函数调用可简化为 function();
[1 q7 i6 X- C; M" j5 G( G; A8 C- ^( \
其次,根据问题描述,可以知道0是这个函数的入口地址,也就是说,0是一个函数的指针。使用函数指针的函数声明形式是:void (*pFunction)(),相应的调用形式是: (*pFunction)(),则问题中的函数调用可以写作:(*0)( )。
9 _( l7 `$ `2 B, U \! ]: b
/ e2 Z, T2 ~8 N$ U 第三,大家知道,函数指针变量不能是一个常数,因此上式中的0必须要被转化为函数指针。
- j6 ^! w9 ]" B1 o3 U
5 q& ?; N4 k3 s! i 我们先来研究一下,对于使用函数指针的函数:比如void (*pFunction)( ),函数指针变量的原型是什么?这个问题很简单,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,清晰起见,整个加上()号。0 @4 d- r* [$ g5 k- X
0 r) _8 R8 D: b- ~, T" K' Q9 p! A
所以将0强制转换为一个返回值为void,参数为空的函数指针如下:( void (*)( ) )。
+ e0 m c( V, H& |& ]- v Y7 @6 [9 I4 a6 F* {8 f- s4 y
OK,结合2)和3)的分析,结果出来了,那就是:(*(void (*)( ) )0)( ) 。
: x) w+ u' A1 h7 E N
" b* v( p4 ]& Y3 L* ^6 |2 |0 C 答案分析:从头到尾理解答案
& w3 z0 L7 W: \' c' {8 P* w2 e+ \
L' X9 }+ \5 K (void (*)( )) ,是一个返回值为void,参数为空的函数指针原型。
. |4 }" N% D3 W1 C (void (*)( ))0,把0转变成一个返回值为void,参数为空的函数指针,指针指向的地址为0. R+ Z# I9 _1 S" Q
*(void (*)( ))0,前面加上*表示整个是一个返回值为void的函数的名字
) H" [; v+ A5 z& z1 b) ^7 | (*(void (*)( ))0)( ),这当然就是一个函数了。
' j! {1 C5 X; n6 b' S& V5 h" H# n/ B# A1 z6 E# G
我们可以使用typedef清晰声明如下:
7 {6 u' X1 F8 W; k+ D1 F( U; S. G D6 O9 \! t. @1 z; X+ O0 a
typedef void (*pFun)( );" K% e7 d; z2 w1 O1 z& j, a ^
1 k0 k) y* p( J8 ~* _ 这样函数变为 (*(pFun)0 )( );* B+ b8 w( Y8 M5 Q. Y# B3 m, E
4 W$ m* m. \6 Z8 ^. y
问题:三个声明的分析' C) f/ h; X7 z
+ H- M2 g/ p: O" l" O
对声明进行分析,最根本的方法还是类比替换法,从那些最基本的声明上进行类比,简化,从而进行理解,下面通过分析三个例子,来具体阐述如何使用这种方法。5 X/ C7 h' x6 t( V/ v8 x
2 i( p. k/ l# `9 ?* V$ `; a#1:int* (*a[5])(int, char*);. F/ V- p3 y8 e3 i; a8 ^. C( H* k1 t
+ p- V- `, d8 P* f& A- Z2 A
首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向“(int, char*)”,很明显,指向的是一个函数,这个函数参数是“int, char*”,返回值是“int*”。OK,结束了一个。:)3 d- S$ v5 ]& W7 V' M
: Q; W# f* c4 l7 p( U7 |#2:void (*b[10]) (void (*)());
8 s( s. ~- n' d" W4 X" A- E
! _% x/ } }5 C( T0 s" J; q$ @ b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注10】,返回值是“void”。完毕!
7 E+ v- D* ?6 w9 z% I$ ?. V9 x
注意:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。" n3 s( U6 O( I3 x' R
6 M T* W: W7 s$ N5 v- g) L p3 v
#3. doube(*)() (*pa)[9];+ X6 D$ f. ^4 s: w
2 K$ N, A: V6 v, ^7 S% E pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”(也即一个函数指针,指向一个函数,这个函数的参数为空,返回值是“double”)。
( L `! q" @- x7 n0 m) s" ] typedef用法小结- -: H- e% c( Q+ r, R: y
在C语言的情况下,与C++稍有出入。5 J1 w3 L$ q. `; y+ B4 d9 Y3 u! K6 j
这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:' x) x X9 a/ B
来源一:Using typedef to Curb Miscreant Code
9 J7 M0 @2 a6 H6 x; o2 I5 \ Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。
" t) `" O# V: o0 G$ n typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。
2 X! L* g& g/ i2 X% M' Z, _+ v 如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?
0 |( x% Q! }. V# O/ w 使用 typedefs 为现有类型创建同义字。" S" v% ]5 Z m/ r3 q' c
定义易于记忆的类型名7 I0 b( q9 O- ^6 ~4 J: q
typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:
8 i. ^, ~) z3 ]2 ^4 s8 j typedef int size;( a" n% V; I4 E
此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:$ m1 u5 J$ L4 N) F5 _0 J
void measure(size * psz);. N7 m# r9 y0 a8 Z3 g
size array[4];: G8 d0 ^! p5 X% X; o
size len = file.getlength();
( [3 l' w' R- ?/ P* c: k std::vector vs;
8 h) V3 @4 I. Q$ R typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:9 l7 z# m" _$ _0 n' ?+ I
char line[81];7 Q9 F' \# l0 s8 B8 H
char text[81];
1 {5 @# M. g# W; i* Z) W4 U4 D 定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
" S5 d' [! y4 x6 u" E typedef char Line[81];+ k8 g% f2 {" r
Line text, secondline;
$ d8 T- O% Y7 {& `' U2 w1 n getline(text);
' f: F9 y( [/ n/ i5 m/ C& _ 同样,可以象下面这样隐藏指针语法:2 k. W, }: B3 h" }. W
typedef char * pstr;
; h: ^: N0 V l l+ m# L/ j2 E; I& O int mystrcmp(pstr, pstr);% n. a. Z6 G2 f1 I
这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():
" H8 _7 b; [; F ?8 Z int mystrcmp(const pstr, const pstr); _. Z. M! s4 G8 C) ^% [
这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:- N1 O" {( ?9 A
typedef const char * cpstr;
& c0 y0 y# R5 i int mystrcmp(cpstr, cpstr); // 现在是正确的1 a0 o+ Y3 G. s' ?
记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。
6 r: m; h0 `9 Y, I0 H l 代码简化" Y$ @1 F. @% T% V" Y, g- e
上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:, F' z% Q/ K3 l( [. M) e
typedef int (*PF) (const char *, const char *);4 S3 o% L: B$ r. _. |
这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
% U" t5 ^8 }/ \# [4 x, P PF Register(PF pf);' q2 J. l* n+ Q9 e/ d
Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
" G0 T+ ?1 Z4 D' P: A0 L1 a int (*Register (int (*pf)(const char *, const char *)))
- ]0 n5 x- i5 ]" ^3 M- c (const char *, const char *);$ t1 t5 X* E7 }1 b: ?) `" F: p
很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件,一个有同样接口的函数。" L1 A3 I/ W4 j- e: C6 W* f
typedef 和存储类关键字(storage class specifier)8 H8 _9 p- `6 x
这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:) H7 A( F, N: X8 B
typedef register int FAST_COUNTER; // 错误
6 P* h/ M/ ^7 q 编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。, B' G ^5 H8 |
促进跨平台开发
6 ~1 L& w- H( `, T5 J8 F typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:$ o; a. M& ]' R2 e: ?1 M
typedef long double REAL;
! K4 O; m( ?# s9 ]5 r 在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
3 d! V# V' f c8 ?0 T K typedef double REAL;
; z" l4 H& |: y0 k& U; K ~# ^, u/ } 并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:、8 q6 W& i2 [, Q' w' _. B2 ~
typedef float REAL;% \/ }9 L+ m5 _7 N B+ ^
你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。
3 ^& P$ z7 _* J! {2 ^ 作者简介
1 N7 I5 q& w$ H; P$ M! E Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。
" H$ K# `5 D _6 c/ I: X3 u9 Q 来源二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)
7 E$ F( P$ p0 u C语言中typedef用法
- P: G" J9 C, n0 [; ]2 B v* \7 P5 K w 1. 基本解释* Z0 y3 y! |$ B9 {3 }4 S
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
6 k4 @- o J- A/ Q; |# a: f+ k 在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
1 d! y/ g0 R( q- L, i 至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。) s' i7 }4 U6 h7 w% ^ T
2. typedef & 结构的问题: ^8 y. q: Q7 G) T5 b8 s0 u
当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?莫非C语言不允许在结构中包含指向它自己的指针吗?请你先猜想一下,然后看下文说明:
& L# f# |& \; b3 W7 I) B typedef struct tagNode
- R5 s2 U8 F; ~, [3 e4 N {
2 ?, j# t9 {5 x$ `. j: p char *pItem;
1 G) w4 C2 o% w- U& s5 q pNode pNext;. G# t6 S( W8 e( X$ p5 M- ?
} *pNode;
' M3 A M% ` J, B 答案与分析:/ V# r6 Z; F2 ~% z9 C6 U; Q
1、typedef的最简单使用( H9 D+ k5 Q6 E6 T$ w9 U
typedef long byte_4;! m5 e' f% ?- D# s, |- R* e2 _
给已知数据类型long起个新名字,叫byte_4。
9 d2 B6 A1 t: _7 ^ 2、 typedef与结构结合使用7 i! {8 z' T- V7 M b* w
typedef struct tagMyStruct
9 w# {/ d t& K& q& A0 |* w# ^7 E {5 ]3 P5 X' y3 k! b' ^3 I' Q
int iNum;
8 Q8 T d) o- s' n0 U N9 x long lLength;
5 f e# {" s; P G& c% W; w- H } MyStruct;
$ ?$ U S( R- F- ^ 这语句实际上完成两个操作:* Y3 S4 M& N3 r7 b" V' b
1) 定义一个新的结构类型2 a2 a2 F. r4 c7 }; A3 Z7 W0 d
struct tagMyStruct
$ V) m5 q. [0 i {
* B$ q) E7 [% p, \) s int iNum;1 w( P# a6 l9 r/ ^ {' R P) w
long lLength;3 O5 y% {" _3 p( I
};( f3 E2 v8 d2 X! p
分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。, F6 K' i+ M# g3 B0 ~$ Q
我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
+ k8 T% d7 x/ }0 m 2) typedef为这个新的结构起了一个名字,叫MyStruct。# N% L" }5 q$ N) T- J
typedef struct tagMyStruct MyStruct;; K, n9 `9 I" ]: w1 |$ `& Q
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
. @9 ?5 ~3 O8 ?4 C, N4 M) L, y 答案与分析
# b: J; ^3 n7 ` C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。
" q* D' Y: i$ F) i, M 根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。: n; R! z$ m9 D/ y& e( S$ h3 @
解决这个问题的方法有多种:% E4 ^8 f: \7 ~* b" \8 l
1)、7 n* c# w8 d' a9 a- s- y! Z! m
typedef struct tagNode
) {. h9 u, q& W3 u& w {3 |0 _' h# M9 y r' V8 O
char *pItem;
$ L8 o( M. s+ [0 v; ^ struct tagNode *pNext;
/ ?: d; n& W( B7 x) o" A# x) N: O } *pNode;
) P* _7 X& W4 O 2)、
7 a8 O% u2 L' E' j! T3 f typedef struct tagNode *pNode;; J) Z* x9 p/ V5 g. t
struct tagNode$ y! x' _6 y7 k* d2 d
{- l |& m" J6 F9 ]
char *pItem;- T) z. l5 g+ X& _! ?* u: l
pNode pNext;
. ~) k& y7 a2 Z6 h2 {$ d2 y };; Y6 [( f! J& V) S$ U, Z6 V
注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。3 z' i$ R$ z, s
3)、规范做法:
1 C) N+ \2 N7 S- W6 g struct tagNode
# C0 P1 {/ m$ y$ w" t w( Y/ ` {
5 ?$ Y1 X- `9 l5 i2 D" @ char *pItem;0 k6 [! A8 `3 X, L, B
struct tagNode *pNext;) }; X% `/ r0 ^9 X0 X* y
}; Z2 Z2 ^) T ]: b8 \7 j/ D9 N
typedef struct tagNode *pNode;* a# V2 d! p4 I5 n8 h5 P8 X1 l
3. typedef & #define的问题3 G! |: J. _. b. O9 l7 s
有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?5 H3 c4 ?$ ^1 l8 ?
typedef char *pStr;
% C1 J( J0 T& x #define pStr char *;! {& v4 T/ k$ Y+ n9 F/ n
答案与分析:' X' f. J! Y: V* d3 e# J
通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
5 \2 ?6 S- k0 a) ~( S! a typedef char *pStr1;
& r- ^ n! m" P1 T# |( } #define pStr2 char *;
* c+ w/ ~6 Q; N, ^3 `$ H8 u% @ pStr1 s1, s2;4 E+ l5 _- [! c! ^
pStr2 s3, s4;+ M8 X! V7 Z0 N
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。/ t9 [* V. t: U# Q6 l3 [" D
#define用法例子:
4 K# c& X$ [- O+ s a8 R$ d& X #define f(x) x*x7 V5 d w7 R ~1 F/ Z3 a- A0 S
main( )5 o# p9 S. e0 Q$ p4 J( x) H
{
$ d8 _( e9 Z2 ~ ` int a=6,b=2,c;
& p9 e- E$ g$ b- x) V" V c=f(a) / f(b);. C6 q- b: K. @0 B2 L
printf("%d \\n",c);8 N* i3 w# A5 T5 W' S
}
( [$ u( {- G" k 以下程序的输出结果是: 36。
4 }* s G7 [! }2 Q1 P: q 因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
: o& b3 d0 j5 ~- B' Q #define f(x) (x*x)$ r# Q- l& u8 M- f: l* e7 o& H
当然,如果你使用typedef就没有这样的问题。, w. |* y! Y7 D' I0 e& o
4. typedef & #define的另一例) N7 }5 A; P3 h, U/ o
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?( n6 C' Q6 C1 L3 `9 ~
typedef char * pStr;# ?9 z# D0 P7 }: W. M: m
char string[4] = "abc";
0 A; D9 ^- F- Q: N3 | const char *p1 = string;
6 I) w0 N( e, i* w7 S const pStr p2 = string;6 ?& v# g6 R; n2 z Z( q
p1++;
- }+ Z. j$ z2 \ p2++;2 f9 c. M- v' j! `
答案与分析:
+ X# z: v0 p$ K9 O 是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。
) w7 m+ X# c8 f; P! U$ M #define与typedef引申谈: Q6 n% W% E& ]1 q
1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
! P( Q3 x1 m4 i, z" d# B0 ] 2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
5 }9 S& L5 T5 ` 5. typedef & 复杂的变量声明0 i7 L/ ~- r$ ?$ [) L% T+ Y
在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:5 t; e. Q; Z1 }) i3 H. E# m4 T
下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?( ~9 t4 {! g* ?- p3 R' g3 n* d5 J
>1:int *(*a[5])(int, char*);# ^6 A0 L; Z* d: J% [ D7 e
>2:void (*b[10]) (void (*)());
2 u; c, A4 G$ E$ a: s, ]. T >3. doube(*)() (*pa)[9];4 U3 F: Y5 d* G. y) D8 ]8 ^
答案与分析:) `# P4 r* T9 X# W+ K, [% }! S o
对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。# W9 A3 f8 S D4 S) g: \0 l( b @
>1:int *(*a[5])(int, char*);
0 K: U& E4 c+ M( c9 J //pFun是我们建的一个类型别名& Z9 q5 a8 ? l& O7 F
typedef int *(*pFun)(int, char*);
9 p9 ~5 S; C. O- y+ W //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
% X, F( i3 V- |3 Q pFun a[5];
3 X3 ^" ~* @: f( C3 s. x$ H/ v2 p >2:void (*b[10]) (void (*)());: [0 y( x! B/ f5 h/ k: D) e, \
//首先为上面表达式蓝色部分声明一个新类型9 O( D" z3 ]3 |% \5 O/ L1 z
typedef void (*pFunParam)();
. O, Q, v% @8 E0 f# ] //整体声明一个新类型. L( F) C+ I9 B0 y, D/ b! x8 h4 r
typedef void (*pFun)(pFunParam);
; i( T/ a1 e8 {5 \4 T //使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
* O0 I4 d! E1 x pFun b[10];
' l6 }: P7 N) O& V9 v; e >3. doube(*)() (*pa)[9];
/ Z6 J. {$ |' Z! P) }) v7 [6 N //首先为上面表达式蓝色部分声明一个新类型; l/ B9 W$ n7 v& @4 c! y
typedef double(*pFun)();3 O, H! l, `% Q$ [ K2 w
//整体声明一个新类型: F' W1 Y, S- g E
typedef pFun (*pFunParam)[9];: s* m% U& n: A3 r' k( Z
//使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9];
s: T; b p% D' s pFunParam pa; |
|