你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

typedef 如何使用?

[复制链接]
gaosmile 发布时间:2020-5-31 18:57
1. typedef 的基本使用! o! N8 z- ~0 I7 s) u
6 v) I& `% x# y, X/ T9 ?3 o$ F0 m( _
/ ?2 C7 K5 `. }9 u
1.1 typedef与结构体的结合使用# \1 c8 N5 g. F

- m3 O1 k% o+ j4 M. a$ A3 a( f% Y- Q
8 V# z# g# d/ o; m
typedef 是 C 语言的一个关键字,用来给某个类型起个别名,也就是给C语言中已经存在的一个类型起一个新名字。大家在阅读代码的过程中,会经常见到 typedef 与结构体、联合体、枚举、函数指针声明结合使用。比如下面结构体类型的声明和使用:
  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
    ) A, X- `. D3 p; I; w: W9 j
  2. struct student! j6 e5 A4 B1 H9 A. q
  3. {- r% l4 ~& O9 q1 P( _3 B
  4. char name[20];
    2 B( V3 E3 ?% S, A( l
  5. int  age;
    ( x4 n6 O; ?4 }4 I/ r) M' Y
  6. float score;  U, P4 }& |7 z' \& {  L4 u6 g
  7. };/ W( r3 P) j3 I" `
  8. struct student stu = {"wit", 20, 99};</font></font></font>
    7 ]; J* H" ~1 m  c2 {
复制代码

! \4 e: \% X1 k; Q
在C语言中定义一个结构体变量,我们通常的写法是:
  • struct 结构体名 变量名;' z5 F5 [6 I7 D6 V& o- C9 t
前面必须有一个struct关键字打前缀,编译器才会理解你要定义的对象是一个结构体变量。而在C++语言中,则不需要这么做,直接使用:结构体名 变量名就可以了
  1. 0 B) ^0 j$ X) Q+ f' w* d
  2. struct student$ o( @! ^, s& _+ B
  3. {
    , P, s3 q1 i% d+ f6 l! `
  4. char name[20];
    ' X2 e3 F1 U! I; J$ l
  5. int age;
    5 K4 K4 P# B$ i# Q7 P+ T) n1 E, I
  6. float score; ' X6 B/ F4 ?, }, L
  7. };4 b% t: l* O6 z. H. J
  8. int main (void)# L' ~5 c' F% O% R9 R* s# U
  9. {6 p! i& N" k. K6 O3 W. o+ N
  10.   student stu = {"wit", 20, 99};
    2 K6 q2 [  g# r, F; b
  11. return 0;" O0 P7 L" S, }& u
  12. }
复制代码
1 p. a1 t( N7 V; i' y# a$ O# R" y' z
如果我们使用typedef,就可以给student声明一个别名student_t和一个结构体指针类型student_ptr,然后就可以直接使用student_t类型去定义一个结构体变量,不用再写struct,这样会显得代码更加简洁。
  1. 9 W" w3 z7 `& O: m4 v( v: |
  2. #include <stdio.h>( B6 p, x1 W# r% D0 i& v
  3. typedef struct student5 i+ t) O+ `+ t0 |+ z( u
  4. {
    + M9 E5 p6 t2 r+ X$ T7 L; Q5 M
  5. char name[20];; L4 M; m) V5 c: I% Y9 F2 z8 c
  6. int  age;
    + a0 G1 q4 L8 m5 q6 k- b, R
  7. float score;
    . ?, f" g6 K  b- I0 h
  8. }student_t, *student_ptr;
    ) |& K. K" k4 G% U

  9. ! p3 F! S' f% k& }# L( ~( B/ A
  10. int main (void)
    ( \* r6 s8 G! X2 l9 j4 G  J5 v
  11. {
    7 n! F- K& n- k$ W/ s! ?7 T& A
  12. student_t   stu = {"wit", 20, 99};
    $ y8 A0 e) Z0 ]: R1 T1 s; G
  13. student_t  *p1 = &stu;* h# S9 I4 D5 V
  14.   student_ptr p2 = &stu;
    ; G7 U& f% b$ Z0 J
  15. printf ("name: %s\n", p1->name);  m* y2 w, R4 i. ^+ Q
  16. printf ("name: %s\n", p2->name);
    " C+ _, M' e3 B0 B
  17. return 0;
    " @; f9 e8 q/ Y( e& g' y
  18. }. _1 F- J& l, c; i- m' x/ `( N: M
  19. 程序运行结果:
    % `0 s  t0 b! s3 d, d& V
  20. wit' q3 C8 Y4 b: C9 ^
  21. wit
    * K- k7 T0 P0 C( ~+ J# }( v8 a% s# d
复制代码

3 t1 \) j3 X# T) V
1. 2 typedef 与数组的结合使用3 n0 [: ~& y' l' B' {

) N% |& e% u" y) U1 q8 j9 S% v0 Z
. t& \1 f! n& ?2 d) S1 U0 B
typedef除了与结构体结合使用外,还可以与数组结合使用。定义一个数组,通常我们使用int array[10];即可。我们也可以使用typedef先声明一个数组类型,然后再使用这个类型去定义一个数组。

  1. : E% S1 A  `. ~0 D* W- s* n
  2. typedef int array_t[10];
    3 G2 t( q& X* y! t
  3. array_t array;
    4 O6 W5 K% B" H0 p
  4. int main (void)
    8 s, @/ \8 {5 q: \
  5. {
    % @. h: |: S# w7 d/ V8 j
  6. array[9] = 100;
    ! l" t% q2 h" a- r3 i
  7. printf ("array[9] = %d\n", array[9]);) r+ }* {/ J: Z6 Z( {1 R& w: L+ k
  8. return 0;: p1 p$ b$ j# ~' w
  9. }
复制代码
; |; z. V, k6 _! E1 G
在上面的demo程序中,我们声明了一个数组类型array_t,然后再使用该类型定义一个数组array,这个array效果其实就相当于:int array[10]。

; M: e, ?0 C  I* K# z! {4 a" ~! q
1.3 typedef 与指针的结合使用

  1. 2 u. I8 Z: p; G2 Z
  2. typedef char * PCHAR;2 {5 H( E- O. E! m7 x; `. P
  3. int main (void)
    6 I, w$ l0 A7 _$ ^# q% g! M7 J  H
  4. {, n+ p1 x1 i0 i8 c  ]$ K
  5. //char * str = "学嵌入式";
    * u' t3 b2 x! ~9 M$ a% b/ G# Y
  6.   PCHAR str = "学嵌入式";2 a$ t2 _# E+ H' A- M4 C
  7. printf ("str: %s\n", str);
    5 j0 s: x! ?, O$ C0 M, A# |
  8. return 0;4 W$ @1 ?/ p5 D' V$ m& f
  9. }
复制代码
- I3 n2 [, q5 r, b0 `/ M2 ?( m
在上面的demo程序中,PCHAR 的类型是 char *,我们使用PCHAR类型去定义一个变量str,其实就是一个char *类型的指针。

# }0 y0 k& V% o9 Y7 j+ [2 i5 m
1.4 typedef与函数指针的结合使用- i1 r/ v4 K, A) p) a$ T8 K  [
$ p$ k# b' M+ x

6 x8 v- Q& X; w, }; g0 {
定义一个函数指针,我们通常采用下面的形式:
  • int (*func)(int a, int b);+ e, G; |: J( E9 v
我们同样可以使用typedef声明一个函数指针类型:func_t
  1. " l8 S8 W* P0 @1 R- N
  2. typedef int (*func_t)(int a, int b);6 p7 Y5 Q& A: P+ O
  3. func_t fp;  // 定义一个函数指针变量
复制代码
0 M0 U/ T1 t, u2 O/ D: I8 r
写个简单的程序测试一下,运行OK:

  1. 1 N4 g1 ~! e+ t. Q$ ?4 P: X- W4 b
  2. typedef int (*func_t)(int a, int b);
      P% X% s0 H8 U+ v# }3 i6 ]
  3. int sum (int a, int b)
    2 w/ x/ Z# S' f
  4. {
    1 E! a  c; \; ~+ _- r
  5. return a + b;
    % E4 p4 X0 s, s, e
  6. } ) D" O/ s# R& t5 |
  7. int main (void)8 p6 r2 d2 ~  H
  8. {9 @4 ~( @6 I5 C/ u% j+ z% e; g7 a
  9. func_t fp = sum;
    2 A' ?: W6 H, z8 h+ F/ v' G
  10. printf ("%d\n", fp(1,2));
    ( P4 c" H( u. ~5 E
  11. return 0;
    : H( p3 N! u# ^+ |: P9 D
  12. }
复制代码

* X( Z' T6 f: B+ ?2 b4 Z
为了增加程序的可读性,我们经常在代码中看到下面的声明形式:

  1. ! u" P8 A( H$ X& Z+ f8 Z
  2. typedef int (func_t)(int a, int b);/ f" C: ]6 p5 [. ^, x
  3. func_t *fp = sum;
复制代码

$ F& [0 h" D1 ]) |6 x+ B6 ?
函数都是有类型的,我们使用typedef给函数类型声明一个新名称:func_t。这样声明的好处是:即使你没有看到func_t的定义,也能够清楚地知道fp是一个函数指针,代码的可读性比上面的好。

6 e3 ^, I0 b& P" G# t# V' f' O3 _
1.5 typedef与枚举的结合使用! _' e; _8 G0 c; ~1 f5 _

  1. 0 I, `# f% n* E; `9 S. w4 C, M
  2. typedef enum color5 j' ], P( x3 ?8 E$ z/ Z
  3. {# m+ {- Q  `( ?$ [& C1 o
  4.   red,
    % P$ Q& R  U! _/ z/ O
  5.   white,. }+ _* {8 Y3 S, E
  6.   black,
    ; g  R+ R9 z8 m3 b* O9 }
  7.   green,5 D' y0 U8 d" \" d* U' @
  8.   color_num,! P' x5 p2 L9 ^9 }/ S
  9. } color_t;
    . [$ b" V, ^" j3 {3 i

  10. - \8 P) a% f$ a2 }7 d4 U
  11. int main (void)& z0 E  w3 S3 G" n+ K& C
  12. {/ S- M6 `" X& ~" I  h
  13. enum color color1 = red;/ W- {* t: Q, n2 ^( B; W' R
  14. color_t    color2 = red;& q+ \3 `, K% E# W; b0 d) n
  15. color_t color_number = color_num;4 g. b8 j# B" Y: l
  16. printf ("color1: %d\n", color1);
    ) M" p, Y" n, ]  p- h8 D
  17. printf ("color2: %d\n", color2);* ~$ t3 f/ o) G6 {
  18. printf ("color num: %d\n", color_number);* X6 w+ |  R/ J$ v+ {
  19. return 0;5 L7 O1 t( N* N
  20. }
复制代码

+ a' {: t, _2 u5 y- X; D/ C: S0 i! W3 T! S7 `
枚举与typedef的结合使用方法跟结构体类似:可以使用typedef给枚举类型color声明一个新名称color_t,然后使用这个类型就可以直接定义一个枚举变量。
0 p) B3 Q  q2 d
2. 使用typedef的优势- L2 n2 D& c; y8 Z& Z* b* x3 M
6 R$ P0 r2 }$ G5 a

2 B0 T2 `7 d2 E  K5 y
不同的项目,有不同的代码风格,也有不同的代码“癖好”。看得代码多了,你会发现:有的代码喜欢用宏,有的代码喜欢使用typedef。那么,使用typedef到底有哪些好处呢?为什么很多人喜欢用它呢?

& K# R, ^6 C. l
2.1 可以让代码更加清晰简洁5 O8 F0 S. U' v2 g* v

  1. ( O. `3 x# c5 r  n
  2. typedef struct student
    4 o$ p/ R( g, R, x
  3. {0 l% n+ j( r9 g$ N0 l) O5 P$ n
  4. char name[20];
    4 U1 R) U; A, D; u' A# z8 v
  5. int  age;
    ' a. i4 M- ^3 h2 S' }6 b
  6. float score;% b* z3 Q1 `; @; W
  7. }student_t, *student_ptr;
    & Q$ @5 [- a- F6 S2 r, f/ d

  8. - o' d6 g! Q3 N; ?4 P
  9. student_t   stu = {"wit", 20, 99};2 F) [  J0 E- A8 w- c' B
  10. student_t  *p1 = &stu;
    4 m0 s& f" J6 x5 T% u
  11. student_ptr p2 = &stu;
复制代码

  t: _7 N0 P0 b
7 v4 @* c3 c# H6 M: x
如示例代码所示,使用typedef,我们可以在定义一个结构体、联合、枚举变量时,省去关键字struct,让代码更加简洁。
" `2 x  U  [& ?9 F! |+ F4 s" T
2.2 增加代码的可移植性+ W# [, L" g" g  {/ w
  I2 `9 {! f# |6 O! \! R- ]
) y0 ?) J1 ^: `( t. \
C语言的int类型,我们知道,在不同的编译器和平台下,所分配的存储字长不一样:可能是2个字节,可能是4个字节,也有可能是8个字节。如果我们在代码中想定义一个固定长度的数据类型,此时使用int,在不同的平台环境下运行可能会出现问题。为了应付各种不同“脾气”的编译器,最好的办法就是使用自定义数据类型,而不是使用C语言的内置类型。
  1. 5 U2 }# r/ z7 U6 t, q& x
  2. #ifdef PIC_16
    0 w- H; w; y2 O( E6 Q- L0 g' E
  3. typedef  unsigned long U32( I5 v- ?2 `. R4 c: E8 ?; ?
  4. #else* g  q* j" l2 g% k: L0 S1 B
  5. typedef unsigned int U32  / }& m% T1 {1 S
  6. #endif
复制代码
- t8 T6 v& M6 z4 F  A3 }/ g
在16位的 PIC 单片机中,int一般占2个字节,long占4个字节,而在32位的ARM环境下,int和long一般都是占4个字节。如果我们在代码中想使用一个32位的固定长度的无符号类型,可以使用上面方式声明一个U32的数据类型,在代码中你可以放心大胆地使用U32。将代码移植到不同的平台时,直接修改这个声明就可以了。
在Linux内核、驱动、BSP 等跟底层架构平台密切相关的源码中,我们会经常看到这样的数据类型,如size_t、U8、U16、U32。在一些网络协议、网卡驱动等对字节宽度、大小端比较关注的地方,也会经常看到typedef使用得很频繁。

2 K$ l! L6 f: S' ?9 i
2.3 比宏定义更好用1 C! o& n. i) R4 o3 V3 F

5 O" W) N" y  v9 @) `1 u
5 p: y2 D5 E8 t9 }  @4 a: r
C语言的预处理指令#define用来定义一个宏,而typedef则用来声明一种类型的别名。typedef跟宏相比,不仅仅是简单的字符串替换,可以使用该类型同时定义多个同类型对象。

  1. 3 g3 W  k" s+ C8 ^4 O0 v
  2. typedef char* PCHAR1;
    6 C) n7 b$ ^) P8 Z
  3. #define PCHAR2 char *, ?& C3 ]: X  A! Q0 F5 b8 M& f

  4. ( ~1 m" s4 m: M; @( t; i% g* N+ J  ?' h
  5. int main (void)
    4 D" Y" C1 g$ A3 Q! A/ Y+ O1 f
  6. {
    & A9 w0 O! j  s
  7.   PCHAR1 pch1, pch2;
    & F3 ^+ C( B9 w) x( t
  8.   PCHAR2 pch3, pch4;
    5 Z& V  O% ]1 ^& q, X
  9. printf ("sizeof pch1: %d\n", sizeof(pch1));( X5 _/ `% G$ w7 E+ ?' t
  10. printf ("sizeof pch2: %d\n", sizeof(pch2));; c2 J! y( Y( x  x" O
  11. printf ("sizeof pch3: %d\n", sizeof(pch3));+ _" i+ g+ p+ q: D4 o* d0 r+ Y5 s
  12. printf ("sizeof pch4: %d\n", sizeof(pch4));6 `- E% B  z( D+ r, b% M( L
  13. return 0;  P8 T% V% q/ T, d9 A( `
  14. }
复制代码
8 `9 [  M% C; C9 @  C% [
在上面的示例代码中,我们想定义4个指向char类型的指针变量,然而运行结果却是:
  1. 8 z( \! h: o+ q, Z" x
  2. sizeof pch1: 4
    6 L9 D* J0 x5 z0 i$ v+ G7 v# y: ?
  3. sizeof pch2: 4
    ( Y# c* E# V* h& }# s
  4. sizeof pch3: 4
      m. W. Q) B2 Q. }
  5. sizeof pch4: 1
复制代码
+ U6 c3 l. c' v, H% n
本来我们想定义4个指向char类型的指针,但是 pch4 经过预处理宏展开后,就变成成了一个字符型变量,而不是一个指针变量。而 PCHAR1 作为一种数据类型,在语法上其实就等价于相同类型的类型说明符关键字,因此可以在一行代码中同时定义多个变量。上面的代码其实就等价于:
  1. ' |, N# `: \+ R7 m4 ]
  2. char *pch1, *pch2;
    + l0 b' S, Q# c) R' |0 u1 C# ^3 w1 M
  3. char *pch3, pch4;
复制代码

+ h2 y  J* j1 r2 p7 C) J) B
2.4 让复杂的指针声明更加简洁
0 H' D5 q9 i( A7 X4 z, V
# g. K' q: t& t# m
一些复杂的指针声明,如:函数指针、数组指针、指针数组的声明,往往很复杂,可读性差。比如下面函数指针数组的定义:
  • int *(*array[10])(int *p, int len, char name[]);
    2 E( [4 \$ g  b8 H- P9 Q$ \8 w
上面的指针数组定义,很多人一瞅估计就懵逼了。我们可以使用typedef优化一下:先声明一个函数指针类型func_ptr_t,接着再定义一个数组,就会更加清晰简洁,可读性就增加了不少:
  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
    ; E- G% u7 C" I, e
  2. typedef int *(*func_ptr_t)(int *p, int len, char name[]);; J2 B# g4 S- O; \2 A8 \
  3. func_ptr_t array[10];</font></font></font><b>
    $ z# O. R$ s  b# L- x
  4. </b>
复制代码
8 R* }0 v, X( E9 y$ x7 g

' e, p0 H4 i7 M, v& r0 D) J3. 使用typedef需要注意的地方' K. ]/ e& W4 V: {1 Y% n9 `
2 E' _+ Z% b8 g) g3 o/ W

1 V/ _- I& Z7 Z: R9 X* Z
通过上面的示例代码,我们可以看到,使用typedef可以让我们的代码更加简洁、可读性更强一些。但是typedef也有很多坑,稍微不注意就可能翻车。下面分享一些使用typedef需要注意的一些细节。

1 e5 a9 W0 I0 N2 E: z8 p
3.1 typedef在语法上等价于关键字7 l: g% Y) r: ?0 O/ y# q

9 S* B% t) @0 `2 N

: R2 F3 \$ F8 R9 n$ p
我们使用typedef给已知的类型声明一个别名,其在语法上其实就等价于该类型的类型说明符关键字,而不是像宏一样,仅仅是简单的字符串替换。举一个例子大家就明白了,比如const和类型的混合使用:当const和常见的类型(如:int、char) 一同修饰一个变量时,const和类型的位置可以互换。但是如果类型为指针,则const和指针类型不能互换,否则其修饰的变量类型就发生了变化,如常见的指针常量和常量指针:

  1. * \# i0 @0 ^$ e- s
  2. char b = 10;  X$ n. U- `  G' R+ _1 O" o
  3. char c = 20;
    8 Q$ g, ?7 U' i: _7 p6 D
  4. int main (void)9 y, s. n1 n& K
  5. {  # ~4 n: f& G9 V( R* \! R
  6. char const *p1 = &b; //常量指针:*p1不可变,p1可变
    * }3 |) W& k8 B% b# T/ E
  7. char *const p2 = &b; //指针常量:*p2可变,p2不可变  
    1 k! r2 \  L7 O# [& t' R
  8.   p1  = &c; //编译正常 $ _2 _, m/ T2 Q( w: r% ~- q
  9.   *p1 = 20; //error: assignment of read-only location  
    % o, Q- K" }& c# ^2 B
  10.   p2  = &c; //error: assignment of read-only variable`p2'; v+ E/ _2 ~% C
  11.   *p2 = 20; //编译正常% o- K7 Z' \3 f1 I0 v' T5 W( B
  12. return 0;
    . i; P2 v" b# q1 Q3 F
  13. }
复制代码
! d& ?" ^7 @! W, W" m8 G7 z
当typedef 和 const一起去修饰一个指针类型时,与宏定义的指针类型进行比较:
  1. " A4 y+ B" \( I  S. O
  2. typedef char* PCHAR2;
    5 ]7 u/ z3 i6 `1 \1 @
  3. #define PCHAR1 char * * i* J+ s3 m) g, P  W
  4. char b = 10;
    , W5 v# d/ a' c: s/ R8 h
  5. char c = 20;
    ) l& ~% M9 ~# p% [6 n. n
  6. int main (void)" x" S. S# k3 t8 O' \7 V' f
  7. {  ( {6 c: `( y$ S# W" s
  8. const PCHAR1 p1 = &b;, X  }9 a8 W+ B
  9. const PCHAR2 p2 = &b;
    9 ^/ _8 S( r- N+ n8 |+ {* D: M
  10.   p1  = &c; //编译正常 ) b) M2 t! q, T" M7 x, A7 @
  11.   *p1 = 20; //error: assignment of read-only location  
    : N) K* }0 B5 [
  12.   p2  = &c; //error: assignment of read-only variable`p2'  b4 C0 d6 ]& b# i3 j. a6 ~( b1 B
  13.   *p2 = 20; //编译正常
    / b6 s& z% g: R8 w7 W: \' z  \6 s/ R
  14. return 0;
    8 ^2 @. _2 h1 Z" @
  15. }
复制代码
  e; Q' L; Z" m  x0 q- `
运行程序,你会发现跟上面的示例代码遇到相同的编译错误,原因在于宏展开仅仅是简单的字符串替换:
  1. 6 g' f0 e2 k9 m' o/ y! N
  2. const PCHAR1 p1 = &b; //宏展开后是一个常量指针
    ; X+ n3 O% ~( R: s* W
  3. const char * p1 = &b; //其中const与类型char的位置可以互换
复制代码
# D6 J2 |5 Q) Q/ D) p) s
而在使用PCHAR2定义的变量p2中,PCHAR2作为一个类型,位置可与const互换,const修饰的是指针变量p2的值,p2的值不能改变,是一个指针常量,但是*p2的值可以改变。
  1. & ?4 W& I9 W$ [0 U" N
  2. const PCHAR2 p2 = &b; //PCHAR2此时作为一个类型,与const可互换位置
    - j8 Y7 Q6 ?# V! _% A6 J+ j
  3. PCHAR2 const p2 = &b; //该语句等价于上条语句
    : L4 X* [7 b! ]$ U. W' J5 `  \; i
  4. char * const p2 = &b; //const和PCHAR2一同修饰变量p2,const修饰的是p2!
复制代码
- [$ {- B& O1 J
3.2 typedef是一个存储类关键字
. `) d( f, f; `- {/ m/ s$ N2 t5 s; e) v: C

0 F3 o% |6 K! N* Q# i( n
没想到吧,typedef在语法上是一个存储类关键字!跟常见的存储类关键字(如:auto、register、static、extern)一样,在修饰一个变量时,不能同时使用一个以上的存储类关键字,否则编译会报错:
  1. 4 V- _% W/ S' j2 I
  2. typedef static char * PCHAR;
    . J5 {+ n' e6 l! r( E- |' j  O7 [
  3. //error: multiple storage classes in declaration of `PCHAR'
    ! `4 j( C; ^8 I+ U3 V7 o% P. Y$ K
复制代码
3.3 typedef 的作用域8 R3 T% V9 _2 Z  z# v
% Y0 P  ]2 @3 b  c: ~% j2 c
5 v, _- w) \" `7 }5 h/ x/ U( S
跟宏的全局性相比,typedef作为一个存储类关键字,是有作用域的。使用typedef声明的类型跟普通变量一样遵循作用域规则:包括代码块作用域、文件作用域等。
  1. 0 k( }2 N+ t& X
  2. typedef char CHAR;7 @5 F5 t; V) M; U) i; S" t
  3. ' s- e. M* A9 i3 K8 H4 _7 m' a) j/ l3 P
  4. void func (void)
    6 Z( H6 B  ]! k: S
  5. {
    - L1 r8 t6 |) m/ a+ C; o, d7 M. f
  6. #define PI 3.14" u2 }0 ?: F' R  @( \; C+ j! I
  7. typedef short CHAR;
    7 R) l! K$ }- g2 n; D! p
  8. printf("sizeof CHAR in func: %d\n",sizeof(CHAR));
    4 v! p2 j& [+ D! J
  9. }
    # I, X; n1 p0 o& c

  10. $ u& c' u# H* d: U( d+ r  [
  11. int main (void). f- E: C& {; @
  12. {  - ^8 k$ s) y1 r% v/ n
  13. printf("sizeof CHAR in main: %d\n",sizeof(CHAR));& a, k' {. n$ g& Y5 i5 K7 _) W/ a
  14.   func();
    " B7 y3 `. k4 K' i% i
  15. typedef int CHAR;: A& J# [: O8 z) p# d2 M# N- q
  16. printf("sizeof CHAR in main: %d\n",sizeof(CHAR));0 b7 L( z7 A  Y& V  D) Y9 E
  17. printf("PI:%f\n", PI);  ( T+ \3 E. K( ]  v
  18. return 0;
    3 X# K( A8 \; }) k9 J
  19. }
复制代码

$ _$ q1 C( E, Z+ F$ B7 z& n6 G
宏定义在预处理阶段就已经替换完毕,是全局性的,只要保证引用它的地方在定义之后就可以了。而使用typedef声明的类型则跟普通变量一样遵循作用域规则。上面代码的运行结果为:
  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">8 s5 o  x1 k. u) W
  2. sizeof CHAR in main: 1
    2 D$ m! B8 n, `/ Q. o
  3. sizeof CHAR in func: 2
    1 T2 E* B1 g7 C: q9 I' ^
  4. sizeof CHAR in main: 4
    6 N' J1 p; h, R3 y  l8 n1 D& q, [/ v
  5. PI:3.140000</font></font></font>
    ' a. C1 F0 G6 M. ^" q
复制代码
3 |: I) _) o/ K. |. A  A9 R
4 如何避免typedef的滥用?
0 n  E0 @& R$ W! Q' T* C+ {$ {& U7 \) |6 O- R) l
1 a: b* i, S5 R2 H3 h5 H
通过上面的学习我们可以看到:使用typedef可以让我们的代码更加简洁、可读性更好。在实际的编程中,越来越多的人也开始尝试使用typedef,甚至到了“过犹不及”的滥用地步:但凡遇到结构体、联合、枚举都要用个typedef封装一下,不用就显得你low、你菜、你的代码没水平。
其实typedef也有副作用,不一定非得处处都用它。比如上面我们封装的STUDENT类型,当你定义一个变量时:
  • STUDENT stu;
    / z. o; z% U+ a8 I8 |" C
不看STUDENT的声明,你知道stu的含义吗?未必吧。而如果我们直接使用struct定义一个变量,则会更加清晰,让你一下子就知道stu是个结构体类型的变量:
  • struct  student stu;
    & m6 w% s: }: n
一般来讲,当遇到以下情形时,使用typedef可能会有用,否则可能会适得其反:
  • 创建一个新的数据类型
  • 跨平台、指定长度的类型:如U32/U16/U8
  • 跟操作系统、BSP、网络字宽相关的数据类型:如size_t、pid_t等
  • 不透明的数据类型:需要隐藏结构体细节,只能通过函数接口访问的数据类型

    $ a4 e" W! q( F, w/ X: N
在阅读Linux内核源码过程中,你会发现大量使用了typedef,哪怕是简单的int、long都使用了typedef。这是因为:Linux内核源码发展到今天,已经支持了太多的平台和CPU架构,为了保证数据的跨平台性和可移植性,所以很多时候不得已使用了typedef,对一些数据指定固定长度:如U8/U16/U32等。但是内核也不是到处到滥用,什么时候该用,什么不该用,也是有一定的规则要遵循的,具体大家可以看kernel Document中的 CodingStyle 中关于typedef的使用建议。(在此感谢“裸机思维”的推荐)
  U0 b& a3 z! t# L
赞 收藏 评论1 发布时间:2020-5-31 18:57

举报

1个回答
李康1202 回答时间:2020-6-1 09:37:45
谢谢分享
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版