版主
主题
帖子
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
概述8 L$ c W3 z' y. u4 O
* d( O8 }, L, H9 \2 o 在很多情况下,尤其是读别人所写代码的时候,对C语言声明的理解能力变得非常重要,而C语言本身的凝练简约也使得C语言的声明常常会令人感到非常困惑,因此,在这里我用一篇的内容来集中阐述一下这个问题。
" D$ r U0 L! s! D2 G; L8 y
+ M9 R: d5 N! H9 S3 C 问题:声明与函数
+ P$ `6 e6 k `! t' ^7 F+ R9 h! |
0 i! ]( I8 ^# ?$ O) {6 k 有一段程序存储在起始地址为0的一段内存上,如果我们想要调用这段程序,请问该如何去做?
5 a& o; J6 }6 W( A( ]* Y: A, O! x( S W' D' {0 w- W3 r3 |! m
答案
7 P& U$ I% B. ?: x1 r1 S+ _
. O1 ?* H" l( |) t( J 答案是(*(void (*)( ) )0)( )。看起来确实令人头大,那好,让我们知难而上,从两个不同的途径来详细分析这个问题。
1 H( f; I5 M# z1 u6 d+ o
( ~, J+ d6 d0 u$ v6 p) D 答案分析:从尾到头
/ [& i( y3 ^7 g+ j% c5 Z' G4 ?& T( y1 V% Q( |' O
首先,最基本的函数声明:void function (paramList);- p0 r( E# x6 v* z
1 M- V& t/ Z5 m6 T
最基本的函数调用:function(paramList);
4 b1 P. E# P v- p( ~; Q+ y7 @8 _! J s; @ V. E
鉴于问题中的函数没有参数,函数调用可简化为 function();
# |/ `0 H. h1 h1 h: }6 r. Z3 L$ \3 F/ ~* W. L1 }
其次,根据问题描述,可以知道0是这个函数的入口地址,也就是说,0是一个函数的指针。使用函数指针的函数声明形式是:void (*pFunction)(),相应的调用形式是: (*pFunction)(),则问题中的函数调用可以写作:(*0)( )。7 b; g7 A+ c. j5 R+ I2 K
8 b' v+ A. i; K) ^7 z4 e* W2 O
第三,大家知道,函数指针变量不能是一个常数,因此上式中的0必须要被转化为函数指针。) ?, h& T& J- w; T2 j1 B
0 Q+ x" O. z: ~$ V) F( z- x
我们先来研究一下,对于使用函数指针的函数:比如void (*pFunction)( ),函数指针变量的原型是什么?这个问题很简单,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,清晰起见,整个加上()号。
4 B ^" s1 {/ a8 S: |, L6 F& T+ B9 D) P
所以将0强制转换为一个返回值为void,参数为空的函数指针如下:( void (*)( ) )。
/ X! s3 E7 L% r
1 v" F t: j; r7 ~5 M) ]% k1 r OK,结合2)和3)的分析,结果出来了,那就是:(*(void (*)( ) )0)( ) 。
3 g0 N8 A) B; Z9 B* R% U9 y( k8 Z
% J0 j$ O$ L( q! A 答案分析:从头到尾理解答案0 |2 F) q S# u
5 r( k, I# Q, B1 R+ g5 g
(void (*)( )) ,是一个返回值为void,参数为空的函数指针原型。
" j4 L8 w1 h! x& B* B1 \0 ~2 U (void (*)( ))0,把0转变成一个返回值为void,参数为空的函数指针,指针指向的地址为0.% r1 Y5 n) C1 a
*(void (*)( ))0,前面加上*表示整个是一个返回值为void的函数的名字9 n0 L+ Y* z3 F* k+ m4 k
(*(void (*)( ))0)( ),这当然就是一个函数了。
R* C& `( A& h2 ]- c) a
7 @5 O/ m, K9 T' U9 y! t 我们可以使用typedef清晰声明如下:* K' u+ |5 H% H; {+ i$ V% u
& ]* U% n: f7 t typedef void (*pFun)( );" s9 S0 [8 S h+ f
6 t8 @/ Q2 }% ?6 U r+ { 这样函数变为 (*(pFun)0 )( );0 b5 \1 @' U0 y* L3 d
Q6 k3 v- M+ N$ _! w6 B# r* F! _
问题:三个声明的分析' n T; n6 m {# _& I
z* W' B1 I k0 C; U7 N
对声明进行分析,最根本的方法还是类比替换法,从那些最基本的声明上进行类比,简化,从而进行理解,下面通过分析三个例子,来具体阐述如何使用这种方法。
" o- c8 q: i2 r8 V3 {* X8 Y* m0 o7 ]1 U. D) n/ x. z
#1:int* (*a[5])(int, char*);
3 j# ?9 c2 g4 O- i) Y# U5 f4 q6 m6 \0 b
首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向“(int, char*)”,很明显,指向的是一个函数,这个函数参数是“int, char*”,返回值是“int*”。OK,结束了一个。:)
a6 l: ~' c: G' a/ w2 Z+ {; t5 R# t- }6 q
#2:void (*b[10]) (void (*)());2 n6 V3 m# b! ]- `. r
8 i2 a! m$ Q! \/ O# h+ J/ ~" s b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注10】,返回值是“void”。完毕!
% X* K: }6 T( e6 a, J, ]0 P- b- r; R
注意:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。 y! A l9 y8 ^+ m# w
# t0 L! o, V0 x1 E#3. doube(*)() (*pa)[9];$ d2 g3 h) k2 L. ^" I' A( o1 x ^
: T6 e3 s+ K: _ @ pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”(也即一个函数指针,指向一个函数,这个函数的参数为空,返回值是“double”)。
8 Y9 z3 ^% f; r- a- y typedef用法小结- -1 `5 {2 a' |7 m6 M
在C语言的情况下,与C++稍有出入。% V) g1 v" {! N9 u! { _
这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:
- {$ V+ u' e4 v9 H- C2 W2 V5 n/ r 来源一:Using typedef to Curb Miscreant Code- ^4 K( t5 I( O+ H
Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。0 n4 g& K$ O6 I! m6 _- Z
typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。
2 a7 [# V7 e! q, v# K+ h 如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?- l# H( r+ u% h- V: p# x
使用 typedefs 为现有类型创建同义字。
" j2 r I, p* U' x4 v4 a* T 定义易于记忆的类型名, {( d% M; a# o
typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如: h1 u) w* i4 S! D/ i, C ?
typedef int size;; B" K. x# o1 {; B
此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:3 D5 k3 Z; D$ p/ n6 M
void measure(size * psz);% ^( s6 Z% e1 ]& S
size array[4];
( d$ G0 J% |/ m size len = file.getlength();
: p) G! `2 w! d# p* T) a std::vector vs;
9 k; K1 }" y/ s- E! A typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:! B5 g, t- [ c
char line[81];
1 _+ d! y2 ?/ h2 ~) t char text[81];& K) M+ B5 V& d; [7 P. x7 B' F+ x" z
定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
6 i: L% ?% w0 N7 l typedef char Line[81];
' J; B9 Z$ w2 ~/ g8 S% t1 T Line text, secondline;
9 T0 x/ r- K% O4 U; s& { getline(text);. }) G& x3 K/ C" K: ^
同样,可以象下面这样隐藏指针语法:
9 B# |8 w, D. ]+ O typedef char * pstr;
6 i+ |4 E; T7 \ int mystrcmp(pstr, pstr);5 P) R0 c% m& L0 P4 |' x6 M
这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():% L9 z; ^! t; \# B
int mystrcmp(const pstr, const pstr);
0 W5 Y0 \! E+ q; Y6 Y; N 这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:
- _1 y7 D4 Z) g7 k6 ? typedef const char * cpstr;
/ ^- h1 \4 z5 Q0 i; v int mystrcmp(cpstr, cpstr); // 现在是正确的7 q' l* I0 P; f
记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。: @" ? ` ?0 [0 i7 c- `; z0 V
代码简化2 E+ @ X, n Q& |5 Y c. E
上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:
k, B! {! A3 F" d2 M+ V typedef int (*PF) (const char *, const char *);/ q! @* y9 ^" ?
这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:$ q2 p: q# v+ e- X
PF Register(PF pf);
" F9 W) i* s* U( F Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
+ y0 V3 ~. t+ N5 r3 \- q0 z int (*Register (int (*pf)(const char *, const char *)))0 W% b$ L/ d. P& t
(const char *, const char *);) |2 A7 i0 H+ y
很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件,一个有同样接口的函数。8 F5 j- z P5 U2 H# G5 t0 C
typedef 和存储类关键字(storage class specifier)0 k. p8 G6 {$ K* U
这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:! r) A* O7 |, F- {( y& H
typedef register int FAST_COUNTER; // 错误0 t4 Z! {& \1 S! K, a
编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。
1 g- m3 [& O' V2 r. D: C' c! x 促进跨平台开发" w5 j7 i! l7 b& d( r
typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:
5 \! w! P! Z4 N L) F6 U2 Q7 i typedef long double REAL;* s8 M: L6 _2 G" j8 N9 c2 @2 F4 P1 ]
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
& r' v6 }( g, o( ]4 h6 ^ typedef double REAL;# ~( n* S. i/ }. t9 y# W
并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:、
1 F5 K8 d: o- P" f Z typedef float REAL; i. Y- i; F% U* }- v0 {0 C( E
你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。9 [) [2 V, D7 B
作者简介
& k" O5 {, ~8 V Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。* r+ a$ Z4 P. N4 E3 F/ M
来源二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)5 q1 i8 u6 c- Z
C语言中typedef用法
( |8 t& _& f! x) N 1. 基本解释
, g/ S/ X* }" r typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
8 Y4 b7 y3 c! ]/ K* `2 O/ c 在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
7 h* g, T/ M. G& @$ ?' m, S 至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。1 s4 P: c* m6 P+ d9 U& w, F" s
2. typedef & 结构的问题
2 G$ |% n w2 t% x1 d6 D$ j# Z 当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?莫非C语言不允许在结构中包含指向它自己的指针吗?请你先猜想一下,然后看下文说明:
& ~0 ^ d1 a0 r typedef struct tagNode" m! R* f" i* {
{
% m% R/ {3 N" Q0 F' @" d char *pItem;* t3 g) [4 w- N' ^. f; T
pNode pNext;( k; F- d9 U9 x5 I. y1 g
} *pNode;# D j& |2 }% K
答案与分析:
* _2 Z1 L6 v: W 1、typedef的最简单使用
^2 v z6 U' ~/ V, t# ~ |/ y) A" ] typedef long byte_4;3 U1 T$ z2 F4 L! K* G+ D4 Y* r( K
给已知数据类型long起个新名字,叫byte_4。+ z6 F+ M7 C r4 u: i% R0 \
2、 typedef与结构结合使用# K4 s1 e% e2 @+ ?& P h! p/ Z
typedef struct tagMyStruct2 l# L+ ^% V% F0 |3 ^
{# S- {) ^" n% G/ k- ?& }* M& g8 Q
int iNum;* h1 M, Z. S V M N
long lLength;2 P$ b8 H8 k2 E' F5 ~) e
} MyStruct;% d' k: v# ~9 s+ R$ [/ I7 r6 g
这语句实际上完成两个操作:
) e7 \+ ~. G3 U3 O# Y 1) 定义一个新的结构类型. e1 N5 X. X& `. T' a) N
struct tagMyStruct
% t! ]+ ?" x. I% {" G3 L1 r {
3 M' ~, x2 L5 }6 _4 o int iNum;
2 e/ I" T. }7 K$ u8 ]! b$ p long lLength;
, v5 o- m7 r7 g8 n) c3 [ };0 Q5 M1 {, _3 g4 [& K
分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
! p$ _3 k+ w* y! ?1 Z3 h n 我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。1 G: u. Z: |# K I
2) typedef为这个新的结构起了一个名字,叫MyStruct。3 o4 h. n) M J5 I/ C- b( G6 c
typedef struct tagMyStruct MyStruct;$ a" y% K7 h8 s; X. K. ^: p6 \ M5 s
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
* s& D9 P) t/ D; t9 F% `" G0 q+ } 答案与分析
6 @1 _: {* l; D- k& F8 C2 Z4 E C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。6 h/ C; S9 q. ^- R
根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。+ ?# ?) ^9 A3 t8 p# \& s9 ]# a6 \
解决这个问题的方法有多种:
9 D8 ^3 n0 @8 G. j. ^* j- v 1)、' y$ I- I6 M" [3 c5 o8 M5 |$ _
typedef struct tagNode
# U2 {- y U' R' I { X# Y, t9 {- Z, D
char *pItem;& U0 L/ a$ I9 V
struct tagNode *pNext;% v: n7 D8 U6 H3 c
} *pNode;
) B' n3 w9 e* l; ?8 D" X6 } 2)、, |, W7 h0 u: y. H
typedef struct tagNode *pNode;8 B6 D1 m, }- q. X% D
struct tagNode
7 z% G! c J; w" k) @ {
5 d/ e9 @7 Q+ j. p: n char *pItem;
8 Y/ o( h d4 b/ Q pNode pNext;
' x* c* \& f/ z/ w' e+ x% y+ M6 n% H };- I6 Q1 U% W! |# E
注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。- Y3 N9 O7 M$ c- W) L$ |
3)、规范做法:
; ~$ S% ~7 h; Z. T struct tagNode# D' G6 f D# R$ G+ t* c/ Y
{: g5 ~7 R* I$ R+ s g
char *pItem;
; K$ c$ u2 j* L/ D struct tagNode *pNext;
; l* l' t2 ]+ d5 ~, c/ y% e z! m };1 }4 d8 U0 P5 s7 `6 s3 D
typedef struct tagNode *pNode;
' o; x8 D4 U2 a. |: G# ` 3. typedef & #define的问题
! g) o$ S. ] `* m: X/ T$ c% o! C 有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?% f9 i2 f/ K3 p
typedef char *pStr;
+ }8 o5 [8 X( |. t4 ]! |6 A- y9 } #define pStr char *;
( Q& Q6 b. H0 N 答案与分析:& Q/ m" P: |/ x" ]5 |4 v( J; Q# y, V
通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:3 a' Y1 z5 v) g* z0 f
typedef char *pStr1;
- S0 ~$ U% r9 @7 b" E5 p! t #define pStr2 char *;
- z( _; u- }- U0 r pStr1 s1, s2;
U, b5 w% t8 @# ^. S$ D5 n! F pStr2 s3, s4;0 }2 |- i% i6 v+ N' c$ \) A
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。; ]' e( E/ C7 B+ T
#define用法例子:
% k7 ?: D& [7 z6 Q #define f(x) x*x
5 p$ E+ E' b6 n$ f, l main( )
. P* m) r: V( v6 @, n6 ^, r) T) b {
8 l: q7 [* @6 ~7 [* D3 X5 M int a=6,b=2,c;
5 Q4 \. r! Q% E+ }7 R$ h' k* r! e c=f(a) / f(b);
4 [4 W9 w) |) F, Z printf("%d \\n",c);
7 l, t* G/ ^5 w% i1 x- l }1 Q" R& G' }, N8 d, I) O$ [
以下程序的输出结果是: 36。
7 @# s1 z2 }' r& i, G# u- }* B 因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:2 Y8 w5 \9 v1 O/ g
#define f(x) (x*x), R6 x4 K" M0 g
当然,如果你使用typedef就没有这样的问题。( a* m$ p) L$ P$ k" p2 b
4. typedef & #define的另一例+ E, y1 R2 ^1 Z3 T7 c; S
下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
/ Q5 v6 F5 n1 [, X9 r typedef char * pStr;, K% P2 b6 I2 s+ b2 V: J: ^6 S
char string[4] = "abc";
$ w. X7 f, E5 M) c* ?+ z H$ Q const char *p1 = string;- R4 ], F, T& m9 g3 u
const pStr p2 = string;2 X, t, f4 q x% n2 }" P6 |- R
p1++;8 [3 D# t: p+ v- i
p2++;
8 t& N4 l! a. f9 l1 I 答案与分析:
/ ?" X! F' J8 T4 r# N; ? 是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。
. b; X$ Y, \& B8 X6 M2 p7 e #define与typedef引申谈
; h; t: M$ M6 f' e+ D* f R3 \: k 1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
4 t+ v- g" ^! P, M: r; r8 J 2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。& n" [/ r9 ]; z$ B3 C! Z' \
5. typedef & 复杂的变量声明0 V; g- y# }( R1 o$ U
在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:; B, ~% V0 b+ g1 R3 @* W6 l; W* j
下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?7 `; N9 }$ P% M
>1:int *(*a[5])(int, char*); p' p" R5 k; P0 z2 O; t( ~# k* Q: U
>2:void (*b[10]) (void (*)());! I ^+ w" n7 P0 C+ ]+ D3 k
>3. doube(*)() (*pa)[9]; U5 f: X6 a, y1 m( T. i
答案与分析:4 n/ Q4 m- N( q& L7 @- G6 c: a
对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。6 E" o C9 k$ U: m" K1 ~* n% B
>1:int *(*a[5])(int, char*);
# C- K. }- R7 n0 I! I //pFun是我们建的一个类型别名; B- p; c9 b' L, S4 ^
typedef int *(*pFun)(int, char*);
) D6 C+ L6 o4 q1 f, W //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
6 A4 i6 l" Z" T2 C, c pFun a[5];1 ^- ]- [3 g7 M0 V6 _
>2:void (*b[10]) (void (*)());
) ]4 ~& f! X) W$ [3 Z% G6 O //首先为上面表达式蓝色部分声明一个新类型) M; N( V; }. U
typedef void (*pFunParam)();7 D- d U/ P0 b; B" u
//整体声明一个新类型
4 o$ {+ r7 t X- O& w typedef void (*pFun)(pFunParam);
* q9 F1 f2 n& g8 X //使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());0 c7 z) r# \7 r) F; e
pFun b[10];
- b3 c0 u8 W4 O$ D( s% q >3. doube(*)() (*pa)[9];
/ J1 A B2 {$ m5 N //首先为上面表达式蓝色部分声明一个新类型
6 c8 a% a; W, Q7 K( | typedef double(*pFun)();
7 ]# C' e) N& O7 H: }! L& o# L |- R //整体声明一个新类型
) `) d: A$ ~# E5 ], M3 R typedef pFun (*pFunParam)[9];1 ]' G+ b2 G1 s6 C0 F
//使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9];( e' ?5 `, o5 V" K0 W+ A7 j
pFunParam pa; |
|