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