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

关于typedef的几种比较不注意的地方使用方式总结

[复制链接]
gaosmile 发布时间:2020-6-17 11:58
又有一段时间没写文章了,今天给大家分享的是c语言中typedef的各种用法;在这之前,想必大部分人和我一样,对于typedef的用法,用的最多用法就是给一个数据类型起一个别名(也就是方便我们写代码),例如下面就是我们常见的写法:$ W7 \8 A! O' L( u( Z

第一种:是在许多实时操作系统中经常会看到的写法:
1typedef unsigned char                                       UINT8;. ^8 ^# W2 ^$ L& j* j$ _
2
  V$ R# Z8 }0 c6 b8 j3 d# y 3typedef unsigned short                                    UINT16;# z* o' m& R' N/ I5 `
4typedef unsigned int                                       UINT32;; t( F7 v$ I! h- s0 w3 U9 S
59 w% \5 U3 b' y8 Y
6typedef signed char                                        INT8;' h, g: R+ O3 J3 y- B2 X( G
7typedef signed short                                       INT16;9 m7 Z5 p+ g) b; U3 F
8
8 Y% F3 ]( h6 v4 t0 P 9typedef signed int                                        INT32;: X$ M9 T' L( G2 s  g+ Y6 Z% _
10
5 j8 C- m$ u; V: s6 q: d, x11typedef float                                             FLOAT;
1 E1 y, l" ?7 c12
5 i- m, [5 ]- D. b0 ^13typedef double                                              DOUBLE;
) V: r8 q8 n$ o; x) A7 @3 U% v( E14
& O8 O2 g$ s4 P+ v" {* J15typedef char                                                CHAR;
5 r  f  ?; \, E+ i% a' U

, `. O0 e3 X+ K1 L. _3 V* P( ]% M

第二种:就是我们在stm32里面结构体定义的时候经常会看到(标准库操作):% \, a/ j$ L+ A+ W" k
微信图片_20200617115120.jpg

4 v. a" `, b2 ^* l  Y6 E
今天我们要讲的typedef用法,平时大家一般可能都没怎么注意到,这里给大家总结一下,以便下次特别是在读linux内核代码的时候(或者其它地方的代码),不要懵逼了,看不懂(到时候和我一样到处查博客,当然自己亲手获取的知识才是真正为自己所有)。这里你对这个用法稍微有一个印象就行,不要死记硬背,理解为主!9 G3 L4 U& E8 P# K7 p# {

一、typedef和const结合的陷阱:
1 l0 R" F3 M3 l# S* O7 ~% M: b& o# K

        这个用法还是比较少见的,而且万一哪天你找工作,在笔试的时候遇到这个,那真是有点尴尬的(如果你不会的话,只能靠瞎猜了)。我们先来看一段代码(你可以先不看我下面代码讲解,自己先看一下会不会丢入这个常规思维里面去理解):2 E/ C6 K- u5 H+ D
+ o3 Y& v# D7 \; _


1#include <stdio.h>$ ?5 B$ t9 x& C$ H* N, b0 \0 U3 |
2
9 I1 z0 n7 X: E' B4 }9 x# B 3typedef int *PINT;
! Y. [5 u8 H9 `# Y5 k 4
/ O: ^# A4 s: `' J5 u 5 int main(void)
% @# A+ w1 m8 H- F$ M2 R# ^ 6  {
6 Q5 p8 g" ]4 h: }' D 7       int a=23;& D! f* k/ J5 ~7 [
8& F; [" \8 }2 C9 K
9       const PINT b=&a;  & |6 ?5 U  Y% i& H/ r; G' b
10       *b=8;: h! Y$ E, R. I( s" R
11       ////////////////////////////////////   
5 [, H4 i( {1 Y12        const int * c=&a;5 Z9 B, @2 s; G
13        *c=43;; v9 v8 p# ]$ q$ W
14
' `; Y. e6 G4 U' q! H15
0 u- Y, M! U, W1 X+ B  ~" X16        printf("the *b is %d\n",*b);: p+ _; u0 S8 _. j# l2 u# N, @
17; A0 k$ A, t2 ?1 b" }
18        return 0;  d" O( `; c4 q2 t8 f/ o
19    }
3 O/ J' j% u: e
运行结果:6 B: b' I2 Q: R: |
微信图片_20200617115124.jpg

      这里你会看到,有一个报错,这个错误很好理解,不足为怪,但是上面的typedef定义的int *类型的指针,取了一个别名叫PINT,然后再main函数里面使用了const PINT  b =&a,并且改变了a的值,但是奇怪的是,为啥没有报错,这是见了鬼吗(因为PINT是int *的别名嘛,所以const PINT b  应该是const int * b=&a,那不是应该报错嘛,可却没有报错,居然能够编译通过,有点不可思议啊)。其实这里就是陷阱了,这里不能这样按照常规的逻辑思维来看理解这个,我们应该把const PINT b=&a看成int * const b=&b,就能理解这里为啥不会报错了,哈哈哈。下面把PINT的位置调换到const的前面来,用法和这个一样。
1#include <stdio.h>
8 W9 |9 L5 [$ ~, r& v- v8 y1 E: d 2typedef int *PINT;
1 u4 f5 v8 h% F 3
/ Y  I4 T$ G* J1 |5 g, i 4int main(void)
* T3 k- ]( w. b9 X1 Z: o 5{+ l) y& [( A* y; w' z
6     int a=23;0 W) d% @! v# K, [
7( A; Z" {2 \5 F) z
8     const PINT b=&a;  5 @6 l( I' U* f3 V0 ^: ?+ M
9     *b=8;
; @8 ]) C; f: j! W+ G4 g5 x10" E: ^2 m0 I9 P
11     PINT  const c=&a;   //这个用法和上面的写法一样8 s9 f0 j/ B# C& z* ^, `  \
12     *c=10;
. C, m) m& h3 d3 g. q! h$ |/ p133 m( a* R7 z0 c" I- s# M# X
14     return 0;
: j* p2 ~) b. C& Y( C15 }

+ h/ p4 j/ z- d6 ^$ ^( M$ F, a4 m

8 ]; b. \: a2 b8 ]  x

       那看完这个,就会有网友会问了,那我要用typedef来实现const  int * b=&a的用法,那怎样搞,这个的话,直接这样写,看下面的代码示例:( ^' c0 m3 p+ W7 W+ _6 o9 W
1#include <stdio.h>+ S4 M) x5 _' q
25 y4 _  j  u/ Q* u
3typedef const int * PINT;$ S' Z& |* i$ e3 i- U
4int main(void)' V) d; ?" h8 `# G# A
5{! w9 V" l8 c+ n3 ]
6int a=23;
* |' ~# D2 M: v 7int c=90;
+ X& `$ y  b! g 8PINT b=&a;//相当于const int * b
- @( b- g( x) P- @  K 9*b=43;- i+ t( y6 C6 `" Z. j% S! h& ^" `: ?
10b=&c;4 o8 a$ P8 g0 k3 L% L6 d7 i& M
11
2 @! P+ `% M# u12) l  q5 a. n4 j- d8 o/ o
13return 0;
- K% s7 u2 }- j. Y/ E14}
+ O5 f- a; J/ n; B, F5 X' u4 L/ ]; ~! n
演示结果:
, Z0 U4 l7 b% [+ v+ u8 O
微信图片_20200617115129.jpg
5 {* P7 Q, o! P# v* `1 R

二、define和typedef的区别:

       我们在平时使用stm32的时候,也会经常使用define来进行宏定义,下面是常见的形式:
微信图片_20200617115133.jpg
( i" y8 P: n1 \' h+ s7 j1 R1 p

        那define与typedef有啥区别呢?下面主要讲两方面:

         (1):可以使用其他类型说明符对宏类型名进行扩展,但是对typedef定义的类型名不能这样做的:
1#include <stdio.h>4 w$ S+ k+ V( t0 z. c9 w7 t8 }9 E
2
. H1 k# h6 x5 n 3#define  haha int
' `& E; Q6 H& l: V1 a  Q* U4 t 4
/ G  v* M5 @: v* I: E3 {2 `- ~4 x 5int main(void), J* z+ I% A9 R- T: E# b  Z. X
6{
$ r) o3 U* F8 | 7unsigned haha i;
( N: q0 j8 |0 ~6 S% @" Y 87 x" {* W( V! [, C" E( K
9# q9 z0 C; x8 S/ b
10return 0;2 R, f& z( L/ ~' `4 n
11 }
3 C9 _1 G' v' h1 V* g& f

/ X1 E0 G  ]/ Q$ k9 X


& N, \3 ~' u2 ^5 [% C" V
演示结果:( v8 F, m/ ~; ~
. R2 M6 P/ ^$ L

微信图片_20200617115136.jpg
6 @) h' k: W( Q0 X- i. v# ]

         然后演示typedef:
+ ^% H+ Y  n: p% }  v- u
1 #include <stdio.h>2 Q4 i1 h( A; ?! L" U
2
9 w" j% T: @- Y 3typedef int haha;8 y# s" Q/ ?) |9 m
4
$ e  U' Y3 `$ l4 I2 n$ \/ H: r7 A 5int main(void)
: B% t1 M9 D9 J: M% K9 }/ m 6{
1 d! ?8 K- O: }& q2 Y3 A2 n0 T+ ~! ? 7unsigned haha i;/ a. }# F: ?" R. B/ M& }
8) Y! u3 n! L7 e: J" [% u. E, z1 r
9
$ f( W5 |5 n& M: J5 G10return 0;
2 x+ m0 h7 I/ M# l4 c8 N7 w3 A11 }

" }2 @# c  U' b+ X  t
0 e" \8 L/ v% Z* C5 k5 \) L  ?

演示结果:

. @/ @% O3 I& [" e; @9 Q4 p, {; J



微信图片_20200617115140.jpg
      (2):在连续几个变量的声明中,用typedef定义的类型能够保证声明中所有的变量均为同一种数据类型,但是我们使用define就不能保证了,下面是演示示例:8 y# U8 S! h; }7 l! a
1#include <stdio.h>6 W4 o8 b* O% E& \, z  g" \
2
9 }( o# S3 ]/ Q% [1 D 3# define  haha  int *2 A( g, |+ k3 R1 h; L3 Y/ s* W
4- E. I8 d& n6 P7 `. r% o! W* ]9 E
5int main(void)9 U+ r7 i& [( U; A' j0 s
6{
- \  E4 ^6 }9 N 7haha  c,d;  //这里就变成了不一样的结果了int *c,int  d
. @3 g0 T8 R4 I, c7 T2 l 8int a=9;
) |. q- m/ @: T9 Q5 \' u 9 c=a;//这里实际应该是c=&a ' k8 ?4 ]% p3 a: t# u
10 d=a;
) ]1 ^- n/ n* E. \5 W" G- V! Q11% a" `9 m0 l( v+ F/ g/ ^7 ]$ a  L
12
7 Z2 l: ~# @$ F+ t; z13   return 0;9 v$ Q% n. w0 i; M4 y0 ?
14 }

( }6 c3 ^, @( R+ w. @# _9 {' `5 e

+ _$ i! S4 W; I# t% z# x

演示结果:
! N+ N. W9 t' U6 f( Y
微信图片_20200617115144.jpg

% L, f3 o8 v9 ]; k3 z

下面我们使用typedef,就可以这样使用了哦:
1#include <stdio.h>( {8 ~3 E( q+ J, ?
2
  C8 N: M- L# Q- j  D7 p& D. L% l 3typedef  const  int *   haha;% S% f' U. w% u4 K, c0 k" N
4
0 h4 x' `- L* H 5int main(void)
7 h& n2 w* C2 \1 U5 j 6{* S4 l# e9 X4 [9 ^, W" J3 ?9 M: x6 t
7    haha  c,d;  //这里就变成了不一样的结果了const int * haha,   const int * d
/ Z/ L. f  {3 h# i2 t 8    int a=9;
$ H. [0 }8 a" F' N4 m0 x 9    c=&a;   p0 D, W# P% K+ u
10    d=&a;0 b7 q! t2 P) @% u2 v1 g
11. T. P/ t/ K7 a- Q8 \3 B& |' l
12) |' d. I' U4 t7 e  y; y0 T9 W
13return 0;
+ v$ o- ?: ~9 V" a1 {14 }

- G/ \1 |, Y4 }/ X

  J* U" i: T" N" T9 O

三、使用typedef来定义函数指针:

       先从一个代码来分析:
1 #include <stdio.h>
9 {: X$ q8 w3 P3 k% V8 } 2
7 S# `3 i+ @6 v7 Q/ b6 u 3 int add(int a, int b) {' ?, d9 X0 n/ X# B  u; T
4  return a + b;8 [6 n  l( {- x, j
5  }
4 z7 R0 y4 x' G" e 6 7 typedef int (PTypeFun1)(int, int); // 声明一个函数类型

. K% `7 u6 Z0 R( T& `9 K 8 typedef int (*PTypeFun2)(int, int); // 声明一个函数指针类型# k$ |5 ?2 Q& t' x8 S
9 int (*padd)(int, int); // 传统形式,定义一个函数指针变量
2 c; N8 m7 n' r" E, C10+ Y: B% |* \# w, `3 H
11int main() {
4 J( ?/ _6 j7 ]# `$ \* N' @7 r12 PTypeFun1 *pTypeAdd1 = add;$ l6 v  N% c2 e! H. I
13 PTypeFun2 pTypeAdd2 = add;
& Q* d; u) S2 w" }14 padd = add;% \8 q6 l4 e  p4 M
15 printf("pTypeAdd1(1, 2) is %d \n", pTypeAdd1(1, 2));
" X* O9 t' y* g/ g! g16 printf("pTypeAdd2(1, 2) is %d \n", pTypeAdd2(1, 2));7 c) K+ T, Y# v1 T
17 printf(" padd(1, 2) is %d \n",  padd(1, 2));( J/ v, j* |. J7 F5 ?7 z
18 return 0;8 k/ Y/ ^  G0 l3 T. q4 ~4 m! k
19}

/ a) h2 W% y3 S3 v  Q
其实这里也是给数据类型取了一个别名而已,但是当函数指针作为其它函数的参数,特别是作为返回值时,直接使用的函数指针无法编译,下面看示例:
1#include <stdio.h>
1 w! S: T7 S' } 2; O. ~" N& Q& a; l: v' f
3void FunA() {8 Z# x" S; E# U5 T4 @1 v! K+ s
4   printf("call FunA\n");
: h6 p5 \4 W* c0 |# _, B1 X" a9 K6 u 5 }8 U" V' e" y) @3 @3 }* x) B" _
6- }5 J( t0 h( I- b0 T
7 void FunB(int n) {
6 K. a1 b: o/ p. F+ b 8   printf("call FunB. n is : %d\n", n);' r7 }  p  s5 @- ?6 \7 T$ u
9 }2 y4 f* t  D" U
10
1 F( ^4 V% c( X1 X  O: `. T* b11 typedef void (*PtrFunA)();9 `/ P, ^6 t5 P4 {/ @
12 typedef void (*PtrFunB)(int);
, @1 K- n- V5 G/ j4 X13$ v1 q! `6 R& M& {( x% V7 _2 w
14// 函数指针作为函数参数使用3 h' k0 {$ b6 S( c7 ^  @
15 void usePtrFunA(PtrFunA p) {
7 R* B) I! \3 w( Y# G16p();# {3 m* j9 ^- O5 O5 g
17   }, P. _, g' |" o
18  A& b" R3 q( I4 ]5 t3 X; d: F
19  void usePtrFunB(PtrFunB p, int n) {1 ?; J4 N. D5 r( L4 f$ z$ x
20p(n);6 }$ U, a1 K9 r2 b) r% Q: V
21   }4 Y5 P) O) D+ k1 a8 j8 ^
22
7 ]" {' J7 M. d, N23  //下面这种语法编译器已经无法识别了. [( k% Q+ {1 I7 f
24  (void (*PtrFunA)()) getPtrFunA2() {  |! e! j2 i5 ?/ ]# W
25  return FunA;
6 J: @) D! \$ j; X26   }
  `9 L( t+ |! G' R27
" z5 q  u4 g5 l% o; w& o8 W28// 函数指针作为函数返回值使用
" J$ o& {1 I$ D29  PtrFunA getPtrFunA() {3 ]$ \) F. Z( t  J; S6 d) j
30   PtrFunA p = FunA;
9 ?% ]4 @. {4 P+ H31   return p;! b+ R) p% i- ]( W) O
32  }* X2 P& ^9 L- i* j& E
33
8 H& S1 \- E# A. j" W# J34 PtrFunB getPtrFunB() {3 B' R( [, X/ q6 L" w. |
35   PtrFunB p = FunB;" }; w! y  B8 e( f7 L  L, V3 }
36   return p;" |3 v, P' b. Y% [. y
37   }8 Y6 v5 \% E4 t  N1 _
38
; C" u3 U: C2 W8 k; J% N39  int main() {
7 i4 U9 x/ G  [# F40 // 获取 FunA 函数的函数指针
, F8 |& R% D. W) y: c+ N! L41  PtrFunA a = getPtrFunA();) u' [+ B8 v4 w* K
42  // 使用 FunA 函数的函数指针
" k: B3 n7 R2 _# o' `" t43  usePtrFunA(a);
; g; w- V$ M6 ]7 g  X" s442 t8 Z& P% e6 L( o
45// 获取 FunB 函数的函数指针; S6 d+ V) M7 k6 b* V
46PtrFunB b = getPtrFunB();' P# W3 T1 w  z
47// 使用 FunB 函数的函数指针! c& K" T$ N% T
48usePtrFunB(b, 1);
+ q: u. x# t- z) R/ |) ?( X49return 0;. o9 [* i1 i$ o' W9 h) x" X
50}
& P% i( d# n9 ^
4 M( t& _" u5 @9 W

四、总结:
     以上是上面的关于typedef的几种比较不注意的地方使用方式总结,希望对你有用。

8 b- t. r' ]. T) y6 i% S
赞 收藏 评论0 发布时间:2020-6-17 11:58

举报

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