版主
主题
帖子
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
概述
# s( W& ^2 T, k" w! U$ E2 B6 g) j8 p% q3 o. ` A4 m" R
在很多情况下,尤其是读别人所写代码的时候,对C语言声明的理解能力变得非常重要,而C语言本身的凝练简约也使得C语言的声明常常会令人感到非常困惑,因此,在这里我用一篇的内容来集中阐述一下这个问题。
% N$ C1 N0 o& F# X
8 Z+ {5 k- B% F( H% b+ j$ y 问题:声明与函数, u( S- h8 e. Q2 m5 [
" i& [) y, G. F4 N# ]0 F) n$ L$ Y 有一段程序存储在起始地址为0的一段内存上,如果我们想要调用这段程序,请问该如何去做?1 m/ a% H; I) A# [* ~
. m* w# p' ]2 j% t. g! x/ N" U U" f 答案3 D6 y" M! L) K, r" Q3 O- d
: T0 }# c' E' m, J0 c( L6 f 答案是(*(void (*)( ) )0)( )。看起来确实令人头大,那好,让我们知难而上,从两个不同的途径来详细分析这个问题。
4 d; S5 g+ r1 V8 H" `7 \. s" o( ^7 f; m7 W
答案分析:从尾到头/ ]# o3 s& e1 D5 B- B7 k ~
2 e; ?* E* }. i1 C 首先,最基本的函数声明:void function (paramList);
# g% M, ~7 H* {4 i
3 e6 F3 U7 F: g( ` 最基本的函数调用:function(paramList); k8 `; `9 p- p8 j* }4 }
9 R+ s# T9 G5 A; q( u 鉴于问题中的函数没有参数,函数调用可简化为 function();% p3 Z. P5 y# @7 I/ F1 y8 k; N
% |# u4 U- \& q* h3 m) k
其次,根据问题描述,可以知道0是这个函数的入口地址,也就是说,0是一个函数的指针。使用函数指针的函数声明形式是:void (*pFunction)(),相应的调用形式是: (*pFunction)(),则问题中的函数调用可以写作:(*0)( )。6 ^) l, \/ R$ u* [* T. t( q! I* D
5 h$ O3 o# I% D" d# e4 F$ K
第三,大家知道,函数指针变量不能是一个常数,因此上式中的0必须要被转化为函数指针。/ O) V' c# s* F
& S6 f& M( ]+ u& G) ~: c- H
我们先来研究一下,对于使用函数指针的函数:比如void (*pFunction)( ),函数指针变量的原型是什么?这个问题很简单,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,清晰起见,整个加上()号。
" P/ | j( U9 T: p7 w
9 W0 A+ o) k6 e! x$ [" n 所以将0强制转换为一个返回值为void,参数为空的函数指针如下:( void (*)( ) )。
3 S& m: _" B# z, R$ q
$ _1 D' D5 q; x' j% X OK,结合2)和3)的分析,结果出来了,那就是:(*(void (*)( ) )0)( ) 。
# q3 Q3 d/ i4 c. ?- e: g0 @2 U2 Z3 [. @
答案分析:从头到尾理解答案
6 y) X3 x4 b) ~
- I! C) E% _3 ?* q) o$ V q* [& X (void (*)( )) ,是一个返回值为void,参数为空的函数指针原型。* x. |1 i5 O B. C" U! c) i
(void (*)( ))0,把0转变成一个返回值为void,参数为空的函数指针,指针指向的地址为0.
6 s# \( M) l: A *(void (*)( ))0,前面加上*表示整个是一个返回值为void的函数的名字
5 c7 p; T0 x7 s- x9 y2 M. R' p( L (*(void (*)( ))0)( ),这当然就是一个函数了。9 e: `2 @2 X7 U+ T/ g4 Q
! t; _) p1 q+ q4 u
我们可以使用typedef清晰声明如下:5 r0 r% | `! Z
0 ~9 M" [, ]2 S1 Z- H typedef void (*pFun)( );; ]% o8 V1 J% t) N, L8 j# B' T' i
3 Q" ?0 t" Q+ `- \# ?9 E
这样函数变为 (*(pFun)0 )( );( J1 S: a9 ~' Q4 E
% j: u' ]$ k: Z
问题:三个声明的分析
* m2 [3 P% C0 L. U& s! P/ r4 M0 y& E* j5 ?/ I$ p1 ~
对声明进行分析,最根本的方法还是类比替换法,从那些最基本的声明上进行类比,简化,从而进行理解,下面通过分析三个例子,来具体阐述如何使用这种方法。1 }9 K5 D% q" B2 G7 S# C0 X# B
}! G: z+ ]$ O- q$ _
#1:int* (*a[5])(int, char*);
: a/ R& Q4 z6 v) g
% G, \# b( e- N. }" d/ J. i 首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向“(int, char*)”,很明显,指向的是一个函数,这个函数参数是“int, char*”,返回值是“int*”。OK,结束了一个。:)% d2 z& P) b. N9 e2 t* g
% a9 C; X# h3 m2 M$ a% ^#2:void (*b[10]) (void (*)());
# y8 G: W; n' w O4 o
0 c, M2 z2 ~3 v l( g b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注10】,返回值是“void”。完毕!
1 m A6 |+ D# \- D7 n9 A% A1 a; U) B* j+ v
注意:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。+ v/ }8 N! A/ `' V0 m
+ t: }2 D. N7 P; _1 g4 w2 c1 e7 K
#3. doube(*)() (*pa)[9];7 p( b. y( X; c* l# M
8 }. P Y( M& u, V. X pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”(也即一个函数指针,指向一个函数,这个函数的参数为空,返回值是“double”)。+ v' P9 N& M5 \, {% X6 ^2 b0 L0 v
typedef用法小结- -
! M4 [1 L* X5 ]3 |) k 在C语言的情况下,与C++稍有出入。
. _# ~! I5 h8 Q' H; D 这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:
7 \2 ~' ]7 n3 n 来源一:Using typedef to Curb Miscreant Code; X0 H% |+ V/ D( h0 K1 I" P1 h( V
Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。$ C6 h) P: R8 ?$ @: `) O
typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。6 R" L( h; D: c, J! L# t" A
如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?
" B2 ^' Y0 g2 ~$ Y- V( `9 C$ d* F' o 使用 typedefs 为现有类型创建同义字。
; B4 `1 {) i. b 定义易于记忆的类型名7 B9 S6 m* n$ s2 W$ V
typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:
/ @& h, `% |1 g7 B( t" `6 ? typedef int size;: x) B0 j$ Y4 g$ i, J3 Q
此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:3 c7 o; a% ?0 ]: z& x
void measure(size * psz);
4 @" K$ } {' o8 N size array[4];9 [) L8 h3 v7 Y$ ^) s
size len = file.getlength();
0 g* ]( m* p" @1 x5 S std::vector vs;
/ a- | P2 C- A) i: ~4 _2 v* } typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:
+ K. ]$ V3 H% [; j0 ^+ [ char line[81];
5 m% j8 J, S6 R1 v char text[81];
2 @* Y) `; Y( l0 p2 h! W 定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:3 n z2 M: G2 [4 I/ ~7 j
typedef char Line[81];
, J2 C* a' \- K, e" D Line text, secondline;
( ~$ `' M/ t, v. | getline(text);
2 n3 }" G* Z, o! O: A3 K 同样,可以象下面这样隐藏指针语法:. ]. f6 U5 e; c& n2 U5 y
typedef char * pstr;2 M( E3 G/ q5 v4 m: k- K+ r
int mystrcmp(pstr, pstr);5 I. O3 m, h6 P1 N
这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():0 k8 ?8 w2 r, J; Q
int mystrcmp(const pstr, const pstr);
! l1 w* E* H8 {0 E! b' t+ l, W 这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:
: {# I' n) m# V4 }% B typedef const char * cpstr;
$ l( T) D6 }. z& c) b$ p int mystrcmp(cpstr, cpstr); // 现在是正确的
# ~4 P5 r; Z! w5 O) A& h 记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。" d6 c# r& h( `
代码简化
6 Y G$ I( i7 g: H3 W& B V$ B 上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:
! e1 A$ k& y% g* L3 y: |9 s6 T typedef int (*PF) (const char *, const char *);' `0 {# X. N* c4 `+ W7 `+ {
这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
! {2 U7 n3 B! h" l5 c( g+ i6 ^; R PF Register(PF pf);$ v, D5 x* }# F8 ]9 i
Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:4 S- r. A0 L r1 c# h
int (*Register (int (*pf)(const char *, const char *)))0 G6 Y, g i" ~ V8 u; i# H
(const char *, const char *);1 b ]4 b- H2 x& i/ N
很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件,一个有同样接口的函数。
, l# ~' s/ @5 W8 P typedef 和存储类关键字(storage class specifier) Z* r9 W& b0 @
这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:
$ c5 R$ W; }* B, K typedef register int FAST_COUNTER; // 错误4 n/ V% t c9 X7 r! l4 ]* \, J
编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。* B3 b0 G1 x9 D F- i1 d( d& s
促进跨平台开发) ~& H4 w O! c' D! X' e$ |7 B! w
typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:
4 |, r# p( V2 E w; p% H+ H typedef long double REAL;& b0 T0 O/ n2 q0 ~
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
% ?) A5 L6 L# [1 H typedef double REAL;
]$ y: d1 N# I0 @( ^! X 并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:、! V) u, h0 k' ^0 h8 z$ e( ^" O
typedef float REAL;' Z( V& Z' d, ~8 Z. S3 N1 y
你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。
6 c8 w. y# q' I8 |7 e# M1 J' O& C 作者简介
' x: K) E, T4 K Q/ k2 @7 ` Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。5 ?( j# Z" k4 g# |& o0 \
来源二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)$ A" i6 J7 \% r# c
C语言中typedef用法: [" L1 {& k+ M @9 ]% c
1. 基本解释
3 B0 U; A$ E9 y. h- s- @: i' E typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。! {" n5 q! ~% }1 d& r( N0 Z( c# l
在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
* B5 H& J( h, ]4 q0 P 至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。. z) z Y2 q: V; f% B9 o% u
2. typedef & 结构的问题; X& P* P8 `* c
当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?莫非C语言不允许在结构中包含指向它自己的指针吗?请你先猜想一下,然后看下文说明:. k, k- n0 P- |* X, d. o
typedef struct tagNode( ~' G; L7 o, m0 o) j* M
{( B) [1 i* F- A5 p1 ~0 R8 k
char *pItem;# x2 h6 c. s& z5 r" o* W: Q! h* T
pNode pNext;9 f8 d% l5 y5 c
} *pNode;' `7 j ~* V) d. R& H
答案与分析:6 l' d% M! F) k
1、typedef的最简单使用4 C. y1 T9 Y% c. w4 f
typedef long byte_4;
4 K. k& P0 R; Z1 U5 W% S1 c0 L: q! H 给已知数据类型long起个新名字,叫byte_4。$ ~* c) K) Y# x6 G' K8 _ Y7 W7 l
2、 typedef与结构结合使用8 I9 n8 O: g- x( A2 o8 D
typedef struct tagMyStruct
! a$ @, K8 q7 `+ v; b9 _0 C+ t {
6 r5 K" F" j/ j! r int iNum;1 k% {3 ^6 e2 }1 t9 `1 m a
long lLength;/ i+ g& I( k0 M [/ f! u
} MyStruct;
: h, y+ U6 |1 ]- ^4 m* Q% u2 m 这语句实际上完成两个操作:
3 {. d- ^" _' N0 w* {! x2 q/ {7 B 1) 定义一个新的结构类型! u) k8 B' i+ L* J9 p f) [/ f$ B+ m
struct tagMyStruct
* L) N+ a8 d l) i' @4 m {
. Z( n- |- G: O( _3 D; ? int iNum;
( j4 A/ M- D7 v: r long lLength;
+ ?( t. {" J. o0 @ };
- }* y& X* V, D" a4 H& y0 i, b 分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。2 I4 V8 d/ C4 b
我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
8 D( u0 k1 w, a7 k+ Q 2) typedef为这个新的结构起了一个名字,叫MyStruct。. J, U, c; B/ D f" B* Q
typedef struct tagMyStruct MyStruct;1 U* r5 C$ C! G
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
3 e Y% V% Q1 [! f$ A 答案与分析
8 H; k4 m: c* T- R! X C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。( k( b% U/ ]: u( Z! Z; m
根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。
* [% p/ \) f3 C, ]3 \! Q 解决这个问题的方法有多种: q5 i( V- B$ S, C
1)、
$ D; j- b/ [4 q& H, y$ `5 b4 t typedef struct tagNode
7 @, X& w) w. X, A1 \3 V' N7 W8 D {( w/ L$ u/ I; `7 I3 r3 x4 w
char *pItem;
, I' K, Y1 Q) q- q- L struct tagNode *pNext;# E% P8 ?' t# y( R. L
} *pNode;
- T1 L; {5 u% V) C' b# h 2)、
8 C! o' X! ~( t% c, e" _ typedef struct tagNode *pNode;
" Z( g4 }1 R/ Y) u6 M$ P struct tagNode) |7 ^: t* s7 T% e4 \% k W! {+ b
{
( A- _- g$ r3 ^ char *pItem;; p7 T( i6 H! z+ n: I7 B' n
pNode pNext;
6 b# b3 |' a0 T& g };
& [& G9 I* J( E4 ? z 注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。$ K' w; H% M$ {- N% e3 C" {) A
3)、规范做法:. J- |9 P5 Q J; ~( p3 O# O
struct tagNode, I4 o8 l, n$ O4 U+ P
{6 D' Z) \- _( t, u2 h
char *pItem;
4 M& }0 d: w7 k7 }0 _& } struct tagNode *pNext;
! v. V1 S W! Y3 A2 n2 g4 P% [ };
9 I" b8 ^" q# E5 ?; f a' c typedef struct tagNode *pNode;
, Q K' A+ ~9 \6 D4 o. H! J 3. typedef & #define的问题
& i M" ~% f5 G3 ` 有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?* v/ j$ s& }2 _' c9 @5 e
typedef char *pStr;
7 C% j' k) G* x+ \0 Z #define pStr char *;
9 S% r2 M) D; A5 m8 } 答案与分析:/ W4 L, L' p( d( O- I
通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:" `/ n2 @7 o5 z( c
typedef char *pStr1;5 V, n2 j6 F3 L4 x& d( U
#define pStr2 char *;
/ m; y3 E/ T+ o. J pStr1 s1, s2;
; t% G; {) g9 a+ W7 T pStr2 s3, s4;) E0 F2 ^" f) e
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。
( e3 k' q% O4 b9 Y P) ^! r- A: R #define用法例子:
t6 R) s* L( a #define f(x) x*x. k3 Q: w" M& d5 ], v5 q4 q K
main( )
; k! h7 M& G. @! F0 s" B' a {
; O' c! W( z% X7 _7 I$ u) X int a=6,b=2,c;7 p- Z/ v# N3 D$ r- Z# E4 N7 r
c=f(a) / f(b);
1 X0 e" [1 {; N" u9 \5 Q5 c% P printf("%d \\n",c);% l# Z2 a" G8 l6 _4 n, ~) j
}3 E: m D& d. ~8 Y& w5 T
以下程序的输出结果是: 36。
. T+ f2 P( _9 \: d; W: K& V 因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
# A8 t) @5 b6 k" o+ J #define f(x) (x*x), `5 O& q& X4 r; V
当然,如果你使用typedef就没有这样的问题。- m# N: c! ~( X4 X0 Q8 E3 H
4. typedef & #define的另一例$ O O! a( G* X3 m/ N& {) b
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?7 v w/ [- f/ Y6 u
typedef char * pStr;( |' y6 |6 N: ?; ?
char string[4] = "abc";
' T) G P5 b# z. p6 {$ W const char *p1 = string;3 y2 C L1 u6 O. Q0 {: y& Q& Z
const pStr p2 = string;
' Y+ B* ?* v1 N* B- f8 M% f p1++;0 z/ U" s0 p# ?( ~
p2++;
# \, J9 g5 ]' w! M8 s7 T! } 答案与分析:
6 p* S! [5 e$ o: H& n' ~" \/ w 是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。+ G2 u& R$ y2 m
#define与typedef引申谈
+ z. T( @2 y$ q- b: ~# H7 X! O 1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。* N) ^8 z+ B2 }& q1 H
2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
6 C3 W+ |4 o5 K 5. typedef & 复杂的变量声明) a. `% Y; \3 I# D4 B/ G. I
在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:7 `( J; a2 X/ B9 \4 U% X
下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?9 a: X" V$ L5 v9 l$ T; ^1 g
>1:int *(*a[5])(int, char*);
" ~: V9 D0 D0 z >2:void (*b[10]) (void (*)());! \. B$ z0 M- @8 q% z) F4 L4 B
>3. doube(*)() (*pa)[9];
. f. |. K2 Z( F. W 答案与分析:, C. ^- j( ^) L% }3 \
对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
! @% [2 j) ]- E/ J* Y7 Y4 e >1:int *(*a[5])(int, char*);
8 d, f7 X& F! M$ N //pFun是我们建的一个类型别名
+ q' `# Q2 p0 M9 W% u" J: k' N0 C typedef int *(*pFun)(int, char*);0 D N9 N( S1 ^8 {. p& ]
//使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);+ w$ d. e, d9 L
pFun a[5];
- m9 w, D( R5 A+ F" u: H >2:void (*b[10]) (void (*)());
/ M$ c( b" M& S& q/ }1 i+ G //首先为上面表达式蓝色部分声明一个新类型% d# ^4 l' H6 `0 x8 |
typedef void (*pFunParam)();; p% Y' H: z: g! D3 v) ~) _
//整体声明一个新类型
3 A8 _6 t8 O# L8 q# ^6 a O/ ? typedef void (*pFun)(pFunParam);: d1 _8 [! L; o1 _
//使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
& u8 h' h3 a% w; R pFun b[10];3 H* H6 F4 e' b% t) J
>3. doube(*)() (*pa)[9];
- \2 T, u+ Q4 d4 { //首先为上面表达式蓝色部分声明一个新类型
- |; {7 J! \% |7 K, J typedef double(*pFun)();
+ I3 n/ {6 P! d% C //整体声明一个新类型! x& Z4 d. \3 R* q+ N
typedef pFun (*pFunParam)[9];. M) b/ f* l2 ^ Q0 Z# ?
//使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9];& B9 S7 |! H6 C; T
pFunParam pa; |
|