|
我是老温,一名热爱学习的嵌入式工程师
8 X7 r1 ~3 }7 _% m关注我,一起变得更加优秀!摘要:不同的项目,有不同的代码风格,也有不同的代码“癖好”。看得代码多了,你会发现:有的代码喜欢用宏,有的代码喜欢使用typedef。那么,使用typedef到底有哪些好处呢?为什么很多人喜欢用它呢?, W! M7 C8 v9 p3 d7 F$ E# [' R$ @
1. typedef 的基本使用1.1 typedef与结构体的结合使用typedef是C语言的一个关键字,用来给某个类型起个别名,也就是给C语言中已经存在的一个类型起一个新名字。大家在阅读代码的过程中,会经常见到 typedef 与结构体、联合体、枚举、函数指针声明结合使用。比如下面结构体类型的声明和使用:- a; Q3 j5 ?' s, G' y+ k8 J6 Q3 G
struct student* x2 U" v7 l1 l! p$ D" [
{
" D" a2 L) Q9 X9 A% S6 ^ char name[20];- H: f/ ?- k' n0 K& p3 u
int age;% k3 {% j. {0 v3 y- C6 j6 M
float score;7 L& I' Q) V+ _, M
};
, j* L' b( r6 cstruct student stu = {"wit", 20, 99};
/ `, _5 J4 j9 c+ |0 a( z1 q在C语言中定义一个结构体变量,我们通常的写法是:. [/ f) H" ^- d# H& H
struct 结构体名 变量名;! Y ? [8 m% O6 [
前面必须有一个struct关键字打前缀,编译器才会理解你要定义的对象是一个结构体变量。而在C++语言中,则不需要这么做,直接使用:结构体名 变量名就可以了6 o% A, U2 ~ i2 \8 t z$ ^
struct student
( L% u- H5 m4 ^1 I& v) R+ }$ n! i{
; @& r8 {4 `- c D char name[20];$ [% _, q* a' b, y& D: Z
int age;9 A3 U1 B6 b; J2 J8 T+ y' s( `
float score;
% @/ |5 A$ Y$ a2 m' m" `9 m};
( l5 L" w; _+ K h0 ~, Sint main (void)
# T! l0 B; [2 N/ V% V. ?% \. I* o# c{- L$ h1 `3 h/ j7 d) ]( @8 K$ N( `6 O( p
student stu = {"wit", 20, 99};
8 K/ K8 m/ h# s% U( |" J t4 f return 0;
6 z& ]4 A6 g. G}
. H0 N5 _) j: B; F1 I6 p3 g0 F如果我们使用typedef,就可以给student声明一个别名student_t和一个结构体指针类型student_ptr,然后就可以直接使用student_t类型去定义一个结构体变量,不用再写struct,这样会显得代码更加简洁。
9 v9 C/ ?" c9 K9 z#include
' \9 X* R2 a+ \+ `8 S# _typedef struct student
]' F8 Y# D) Y0 g+ z{
) z3 Y4 I# }( F7 ?# H' {( I& C2 U+ ] char name[20];$ M e0 p/ j4 b# F9 Z# h& p
int age;0 w# \4 N/ E3 @- U8 K" a
float score;
0 G% M; d* ?" a: E B1 s8 \$ }}student_t, *student_ptr;1 {0 y; Q+ k5 P* l! D) v5 J# o
int main (void)& U0 e' m9 G7 v! G( s
{
8 n+ ^. _$ H3 |# E0 q. i student_t stu = {"wit", 20, 99};
, ]3 d2 {3 ^1 Y/ i4 H8 ~! B student_t *p1 = &stu;
@0 @' ^/ ^6 \9 W0 s3 m student_ptr p2 = &stu; I% T- b" T0 _: _! a
printf ("name: %s
# {9 v& B# D& M5 w8 |, h8 }", p1->name);* |+ x& S% N/ K$ R! e% j
printf ("name: %s
1 G6 ]* p, h2 J8 o5 G", p2->name); # a& O: N) j6 w8 r, B
return 0;0 l+ Y0 D3 e8 v, s4 w
}' R) R2 k) W, @; h
程序运行结果:
/ e* |8 u3 }/ W6 H0 G, A) awit( {5 T; ?9 a* n0 C+ H
wit% G" n4 w) q$ g
1. 2 typedef 与数组的结合使用typedef除了与结构体结合使用外,还可以与数组结合使用。定义一个数组,通常我们使用int array[10];即可。我们也可以使用typedef先声明一个数组类型,然后再使用这个类型去定义一个数组。
" ]! ?' D6 V s& j9 dtypedef int array_t[10]; 2 z1 F1 n% S) r* V
array_t array;
% _" T1 g% c& l( v% {1 [+ oint main (void)
% m0 L6 f% z2 u! Y{" [% R- J/ |7 _2 ?& h+ c5 V
array[9] = 100;4 B$ M; H4 J# B6 Q0 o- j1 W- i3 c8 w
printf ("array[9] = %d
* D1 x! s# h3 I+ s2 k- C", array[9]);
9 j% Q. l8 p) M7 {, W' F7 u* k) B; j return 0;
5 R @8 J& c* S3 ?0 l5 g1 k: Q}& {8 ?* z0 \% x8 ~2 m1 I S, N6 g# W
在上面的demo程序中,我们声明了一个数组类型array_t,然后再使用该类型定义一个数组array,这个array效果其实就相当于:int array[10]。9 o! c# C2 _5 V' I& d7 N8 d
1.3 typedef 与指针的结合使用/ l. c9 k, s' c; p
typedef char * PCHAR;. c# Y) ~: b1 ?* v; X
int main (void)
+ p J4 M$ I; M{
* A8 Z6 a+ V: a: \ //char * str = "学嵌入式";
) t# N. b; P7 ?* o. ~% P# S" l PCHAR str = "学嵌入式";6 J8 ^4 {4 N+ V* ]
printf ("str: %s
1 }) k0 a+ V2 N( l+ P! m", str);0 ?5 i6 V/ ]( t9 N a; N) K7 c
return 0;' v* U2 L6 t: n7 p" B( t) V2 A
}
5 Y. k9 Z- _% U. p! a在上面的demo程序中,PCHAR 的类型是char *,我们使用PCHAR类型去定义一个变量str,其实就是一个char *类型的指针。, H; h. M& M9 {$ Q5 H; e
1.4 typedef与函数指针的结合使用定义一个函数指针,我们通常采用下面的形式:# U! ~4 N8 A7 }8 [% Y! R0 _
int (*func)(int a, int b);
+ h8 L' Z+ J$ n: Z# n3 [9 G9 H我们同样可以使用typedef声明一个函数指针类型:func_t
2 S+ y, @# H7 J6 Xtypedef int (*func_t)(int a, int b);
2 w& p* z4 p" p2 H$ H Gfunc_t fp; // 定义一个函数指针变量/ L! j: f% E5 ^- }3 m( D6 t& s6 ?
写个简单的程序测试一下,运行OK:8 S* M5 y1 J7 M/ F8 {
typedef int (*func_t)(int a, int b);
2 [& J+ w% \5 Z0 h" Iint sum (int a, int b)
. t0 T! k1 h- S2 _' {7 `{
$ `! c% s% }: j1 y k return a + b;3 w/ Z! d2 {9 g6 ~: M% E
} 9 z& s: c( T# L0 H% n. R. [* {8 T
int main (void)$ K9 P# K7 u8 m# Y% a
{
2 n( X0 C5 m# ~+ d' x func_t fp = sum;
5 H x& l) E3 ~* ^% H% y printf ("%d9 A* a* o3 X! C2 U A& d0 d* ~
", fp(1,2));
4 ^0 b" w8 |! m6 e+ }, T+ b return 0;
5 p" A. G; C( p0 X}7 o) k, M: [' ?2 h' d7 @( c
为了增加程序的可读性,我们经常在代码中看到下面的声明形式:
. ^# k, R6 k( }# X; C/ ftypedef int (func_t)(int a, int b);0 G! y. F# L% e. E) d; O
func_t *fp = sum;' G+ e x V& c N& X
函数都是有类型的,我们使用typedef给函数类型声明一个新名称:func_t。这样声明的好处是:即使你没有看到func_t的定义,也能够清楚地知道fp是一个函数指针,代码的可读性比上面的好。. @; I! t+ N: @4 S( v/ t4 \
1.5 typedef与枚举的结合使用typedef enum color* @ A7 a" E' D% j6 O0 n# S
{
& ^% Z$ c B) g6 \, o red,% h4 \( g9 B4 e3 I
white,3 E5 R8 C6 z$ `2 q C
black,7 T3 [9 M" u$ [6 @4 y& L
green,6 V' q" _9 I4 N, Y8 d8 }3 a9 V
color_num,# _- J- F5 p6 v0 E- P" [
} color_t;
+ x$ O. V. o& A0 lint main (void)
( h" T7 u9 y" S4 ~{& r! g+ O# Z/ f6 i8 u
enum color color1 = red;
7 w# A& g2 }1 h. `# M, n; c; @ color_t color2 = red;
9 u/ g( k9 d, ?3 H color_t color_number = color_num;
; e; i9 k9 }$ w6 M- Q: r printf ("color1: %d
/ ^, i& v+ h0 h", color1);* c5 l, p) i8 O1 V( Z
printf ("color2: %d
* E4 R2 y! s; {# D4 t", color2);( z7 b3 m' J6 z; ?. p) V
printf ("color num: %d
" c$ A! n! \" U" _: E8 u- j", color_number);7 ^6 y1 Y7 V6 O; o" {. w
return 0;) g* q) I/ q5 ]2 o" J
}3 d4 Q/ W6 E: a/ K, Y
枚举与typedef的结合使用方法跟结构体类似:可以使用typedef给枚举类型color声明一个新名称color_t,然后使用这个类型就可以直接定义一个枚举变量。
3 B5 _7 ~, h# }( g! [ k) G2. 使用typedef的优势不同的项目,有不同的代码风格,也有不同的代码“癖好”。看得代码多了,你会发现:有的代码喜欢用宏,有的代码喜欢使用typedef。那么,使用typedef到底有哪些好处呢?为什么很多人喜欢用它呢?
' A- a! Z" S- U' }: [7 }. u2.1 可以让代码更加清晰简洁typedef struct student
$ P# B U& x. ?0 q/ l# U5 U- B5 {{
8 F- u8 Q4 L, n# D* G7 |' I7 g char name[20];- s" s. e. n D+ k
int age;( V" ^/ z; j0 ^9 g6 R2 c
float score;# _9 w# x% }7 _" R1 P6 A5 ^! K
}student_t, *student_ptr;
! G. f0 P" Z( p+ N% p+ Lstudent_t stu = {"wit", 20, 99};- F& W, C( z: _" t
student_t *p1 = &stu;
. U) o) ]+ b: f6 p1 Xstudent_ptr p2 = &stu;
: I9 _+ s ?% I0 P2 |& U5 ^; f5 y. J如示例代码所示,使用typedef,我们可以在定义一个结构体、联合、枚举变量时,省去关键字struct,让代码更加简洁。
! y3 z; T% e! J2.2 增加代码的可移植性C语言的int类型,我们知道,在不同的编译器和平台下,所分配的存储字长不一样:可能是2个字节,可能是4个字节,也有可能是8个字节。如果我们在代码中想定义一个固定长度的数据类型,此时使用int,在不同的平台环境下运行可能会出现问题。为了应付各种不同“脾气”的编译器,最好的办法就是使用自定义数据类型,而不是使用C语言的内置类型。
# [ Q) M: z' a0 _5 q: r1 g#ifdef PIC_16) F, r7 |: E |: c
typedef unsigned long U32# ]: {5 ~1 {0 _; t7 v. b/ u2 J
#else
. Z" `. G2 H+ t# @2 i/ ~7 \4 Jtypedef unsigned int U32 T. [, k/ h7 Y6 Q
#endif
! b8 j1 F2 X! R2 t2 {1 C# D0 _" P在16位的 PIC 单片机中,int一般占2个字节,long占4个字节,而在32位的ARM环境下,int和long一般都是占4个字节。如果我们在代码中想使用一个32位的固定长度的无符号类型,可以使用上面方式声明一个U32的数据类型,在代码中你可以放心大胆地使用U32。将代码移植到不同的平台时,直接修改这个声明就可以了。( p4 z/ u/ X' d' s
在Linux内核、驱动、BSP 等跟底层架构平台密切相关的源码中,我们会经常看到这样的数据类型,如size_t、U8、U16、U32。在一些网络协议、网卡驱动等对字节宽度、大小端比较关注的地方,也会经常看到typedef使用得很频繁。( N9 b% R4 G- B
; p9 N( J# ~4 U! N% n" ]3 ~
2.3 比宏定义更好用C语言的预处理指令#define用来定义一个宏,而typedef则用来声明一种类型的别名。typedef跟宏相比,不仅仅是简单的字符串替换,可以使用该类型同时定义多个同类型对象。
* e, e+ a, h1 p, E4 _, ]typedef char* PCHAR1;( Y9 T: M" q+ x Z' A6 _
#define PCHAR2 char *
/ x q; u- b9 wint main (void)
6 J+ p, e/ X9 E{
- W- `" |$ m9 S PCHAR1 pch1, pch2;
3 p. L; ]3 V0 c# l, G' y PCHAR2 pch3, pch4;; _- {0 F& m2 D+ c- J% H
printf ("sizeof pch1: %d
9 S, ]# Z/ X( ?3 E# F", sizeof(pch1));$ r: ?* K6 [4 b; N" q6 g
printf ("sizeof pch2: %d
T4 e: |, b; r' Z", sizeof(pch2));
5 y/ M; z& v/ s4 o4 O, l printf ("sizeof pch3: %d9 B& z, V1 J2 L
", sizeof(pch3));. a/ z4 t7 b5 c0 ?6 G8 ?4 _( Z
printf ("sizeof pch4: %d
' q2 }1 C' B( q# Q. r( G* `6 n( I", sizeof(pch4));! k8 j3 Z4 m. E! S
return 0;) z/ ~1 \' ~; x8 b# a
}3 i4 G$ Z3 l7 ^( S2 U2 v' l
在上面的示例代码中,我们想定义4个指向char类型的指针变量,然而运行结果却是:9 f H. l, _! z1 k; O/ T, y. R/ ^
sizeof pch1: 4: r& m) M6 g3 t
sizeof pch2: 40 r+ z" V% O' k c9 C* R
sizeof pch3: 4
6 b) m8 t6 D6 ?) g+ e2 D; Zsizeof pch4: 1; Q$ M$ y! Y* F+ e" ]* o
本来我们想定义4个指向char类型的指针,但是 pch4 经过预处理宏展开后,就变成成了一个字符型变量,而不是一个指针变量。而 PCHAR1 作为一种数据类型,在语法上其实就等价于相同类型的类型说明符关键字,因此可以在一行代码中同时定义多个变量。上面的代码其实就等价于:
' y0 f+ x2 M R, G6 F# rchar *pch1, *pch2;
+ B$ J) k% `6 b/ L6 o1 Y# O# s' `char *pch3, pch4;9 _6 _) B) C( {; \# @4 y( B
2.4 让复杂的指针声明更加简洁一些复杂的指针声明,如:函数指针、数组指针、指针数组的声明,往往很复杂,可读性差。比如下面函数指针数组的定义:* E, u7 N9 S$ l+ Z
int *(*array[10])(int *p, int len, char name[]);
6 [* S+ u$ {6 O1 S% t. w上面的指针数组定义,很多人一瞅估计就懵逼了。我们可以使用typedef优化一下:先声明一个函数指针类型func_ptr_t,接着再定义一个数组,就会更加清晰简洁,可读性就增加了不少:# q# H# P0 w+ R
typedef int *(*func_ptr_t)(int *p, int len, char name[]);
- u) ]' G: `; X( v% ~/ R0 sfunc_ptr_t array[10];
, V1 e, J/ f( K6 J( {1 @3. 使用typedef需要注意的地方通过上面的示例代码,我们可以看到,使用typedef可以让我们的代码更加简洁、可读性更强一些。但是typedef也有很多坑,稍微不注意就可能翻车。下面分享一些使用typedef需要注意的一些细节。" G+ f6 @: O* b9 D) U, R
3.1 typedef在语法上等价于关键字我们使用typedef给已知的类型声明一个别名,其在语法上其实就等价于该类型的类型说明符关键字,而不是像宏一样,仅仅是简单的字符串替换。举一个例子大家就明白了,比如const和类型的混合使用:当const和常见的类型(如:int、char) 一同修饰一个变量时,const和类型的位置可以互换。但是如果类型为指针,则const和指针类型不能互换,否则其修饰的变量类型就发生了变化,如常见的指针常量和常量指针:
8 q0 y& i, `2 L7 Echar b = 10;
) R- w5 c1 e. p# \) ~/ _8 Xchar c = 20;
3 D: C- W, F, L0 }int main (void)
; W6 y4 }; ~2 k, _# ?! b{ 8 B2 i/ f7 @) t
char const *p1 = &b; //常量指针:*p1不可变,p1可变
z8 n1 P {, `( H7 b: ~ char *const p2 = &b; //指针常量:*p2可变,p2不可变
2 J7 n9 a) J, C7 [ p1 = &c; //编译正常 0 @; {$ w5 n3 @: h7 n
*p1 = 20; //error: assignment of read-only location 1 R/ _$ V! i8 I. ?
p2 = &c; //error: assignment of read-only variable`p2'& M. ~$ {9 c( x# ]' G
*p2 = 20; //编译正常8 I: Y: f& W$ l" c6 E; V
return 0;
3 w. P ]0 c4 L+ ^( w8 @3 d2 A$ G}
- j( p4 k' D1 D当typedef 和 const一起去修饰一个指针类型时,与宏定义的指针类型进行比较:
1 p7 Q5 D% G, F" l4 [typedef char* PCHAR2;$ u' u1 O2 } I# t4 j/ Y
#define PCHAR1 char *
/ c; |+ L/ m# }" H) qchar b = 10;
6 i2 t; ]+ n8 j7 h9 c6 _1 hchar c = 20;1 ` h3 V9 ?4 t, Y' e2 O, [. l- |
int main (void)( \. C6 }( y& q y
{
( }. ~7 E, \4 D& s8 v! W8 O1 G const PCHAR1 p1 = &b;& f* `0 P+ N; \3 E/ u4 s& [+ n6 u
const PCHAR2 p2 = &b;+ F* _& Y# @: Y- L5 B( }; ^. x6 e( {
p1 = &c; //编译正常 6 J j# q% E9 [% t+ V [
*p1 = 20; //error: assignment of read-only location . c0 z. }5 s! p. G
p2 = &c; //error: assignment of read-only variable`p2'9 J0 c( b( c! R/ A
*p2 = 20; //编译正常
' e- {0 S7 W O# K8 a, B7 P return 0;
& h2 ?( o$ g8 t/ U7 y}5 z0 c7 v9 r9 j8 U/ P8 U
运行程序,你会发现跟上面的示例代码遇到相同的编译错误,原因在于宏展开仅仅是简单的字符串替换:! y# p5 M+ w' ?( i# ]
const PCHAR1 p1 = &b; //宏展开后是一个常量指针
/ s* O5 Q5 K3 }+ Mconst char * p1 = &b; //其中const与类型char的位置可以互换
, m, c, `+ P% D# F5 R5 W而在使用PCHAR2定义的变量p2中,PCHAR2作为一个类型,位置可与const互换,const修饰的是指针变量p2的值,p2的值不能改变,是一个指针常量,但是*p2的值可以改变。
+ @ p5 `; z5 ^* j4 D1 G8 c1 r) K1 ^const PCHAR2 p2 = &b; //PCHAR2此时作为一个类型,与const可互换位置
" E/ G: W% q# Y% ^! r/ M IPCHAR2 const p2 = &b; //该语句等价于上条语句8 J: C7 \' m+ |& M0 Y, Y
char * const p2 = &b; //const和PCHAR2一同修饰变量p2,const修饰的是p2!& z0 l- k! `# z- l
3.2 typedef是一个存储类关键字没想到吧,typedef在语法上是一个存储类关键字!跟常见的存储类关键字(如:auto、register、static、extern)一样,在修饰一个变量时,不能同时使用一个以上的存储类关键字,否则编译会报错:( ]& l k2 g/ X
typedef static char * PCHAR;
$ x" U2 E5 R# c* I" }! f9 Y//error: multiple storage classes in declaration of `PCHAR'
! \' s8 ^, D, l; x4 o5 \, [3.3 typedef 的作用域跟宏的全局性相比,typedef作为一个存储类关键字,是有作用域的。使用typedef声明的类型跟普通变量一样遵循作用域规则:包括代码块作用域、文件作用域等。1 x+ h; a6 i; Z8 u/ l7 Y5 w# ~
typedef char CHAR;
; O& C9 I$ F& J" Y5 `4 Jvoid func (void)
. W4 T f7 s) f( o" t) C8 d{
) s6 y( b8 H- r! Q$ w C5 S! W #define PI 3.146 ]- P2 v* F" ^! Z4 y9 \$ j
typedef short CHAR;
% W, {& v6 Z1 ]! ?3 @4 F printf("sizeof CHAR in func: %d
- y( m- N5 J7 a/ A6 |! J$ U x",sizeof(CHAR)); q$ a) ]# _6 p
}
0 l! n% s( v* N1 n- ~$ F( bint main (void)
$ A+ ^% t, k6 I# \2 h4 _{
& `) ]# \( a; s6 G printf("sizeof CHAR in main: %d+ m* e: H, k0 _( O' l
",sizeof(CHAR));
0 y. N/ A. Z% M. F( Q$ V. `" b func();; k. K; ~6 K+ C( y( W: N& F
typedef int CHAR;
' \: z5 q/ k+ {* D) P2 ~ printf("sizeof CHAR in main: %d9 W8 z, `! f" U4 X8 E+ [
",sizeof(CHAR));$ V0 N9 Z# A, g$ l
printf("PI:%f1 b+ r) ^! H, V/ w3 ?& V
", PI);
; U/ R1 y& e5 D9 P+ z) w return 0;
1 ?1 s6 Q% [ @. Y5 {) E}% m$ Y6 c& c+ Z! G m
宏定义在预处理阶段就已经替换完毕,是全局性的,只要保证引用它的地方在定义之后就可以了。而使用typedef声明的类型则跟普通变量一样遵循作用域规则。上面代码的运行结果为: U* c; F3 ?5 [# r0 a# Y* Z0 E* [
sizeof CHAR in main: 1, b3 n1 Q0 ^& V+ r& s# B
sizeof CHAR in func: 2/ r% o9 a% z7 g/ T# M/ M# d+ o+ N
sizeof CHAR in main: 4* o. z- T% w B" e$ @- B# \
PI:3.140000+ y" z8 q L8 e' n- Y
4 如何避免typedef的滥用?通过上面的学习我们可以看到:使用typedef可以让我们的代码更加简洁、可读性更好。在实际的编程中,越来越多的人也开始尝试使用typedef,甚至到了“过犹不及”的滥用地步:但凡遇到结构体、联合、枚举都要用个typedef封装一下,不用就显得你low、你菜、你的代码没水平。
' X3 d! s2 [* z7 @3 s6 |1 D+ d其实typedef也有副作用,不一定非得处处都用它。比如上面我们封装的STUDENT类型,当你定义一个变量时:2 l) r2 W' y2 e$ B( I3 z5 L
STUDENT stu;
" ^3 M" T8 G S$ q K不看STUDENT的声明,你知道stu的含义吗?未必吧。而如果我们直接使用struct定义一个变量,则会更加清晰,让你一下子就知道stu是个结构体类型的变量:" P0 w7 ~0 X- i' r
struct student stu;& B. s7 g2 [0 w& Q8 c5 z
一般来讲,当遇到以下情形时,使用typedef可能会有用,否则可能会适得其反:+ W5 j ]. { s% ?
创建一个新的数据类型跨平台、指定长度的类型:如U32/U16/U8跟操作系统、BSP、网络字宽相关的数据类型:如size_t、pid_t等不透明的数据类型:需要隐藏结构体细节,只能通过函数接口访问的数据类型
, n; ^) X0 c2 u" N在阅读Linux内核源码过程中,你会发现大量使用了typedef,哪怕是简单的int、long都使用了 typedef。这是因为:Linux内核源码发展到今天,已经支持了太多的平台和CPU架构,为了保证数据的跨平台性和可移植性,所以很多时候不得已使用了typedef,对一些数据指定固定长度:如U8/U16/U32等。但是内核也不是到处到滥用,什么时候该用,什么不该用,也是有一定的规则要遵循的,具体大家可以看kernel Document中的CodingStyle中关于typedef的使用建议。1 B4 |( R' [2 N# H3 A; J9 H( d
文章来源:网络-END-3 h, K( Z& e5 O8 t* ~" u) B: P" b% J1 K
往期推荐:点击图片即可跳转阅读
3 W9 e- i7 }+ t( F' z# v5 I! v+ Z
5 u6 l: Y2 [5 c3 m* s
$ ^& _) }8 h& S ( y6 L% u3 @+ y- I. }& n" R
9 v2 m/ Z8 J8 e3 i# G. Z 4 [+ @5 }; F' d; W7 c
ktxk1fuf3t3640361516.jpg
. b& ^( e# \: _# P% {& A5 A% k$ G
8 H; V( ^, K. `: j 嵌入式软件,能用“低代码”的方式进行开发吗?" f; o+ I9 x; U+ h3 X
" V' R- f O0 h2 @
2 A% G5 j# M6 A! J
& I' C/ {# [: y/ H0 B
, v2 X5 @0 l r' r; C 2 t7 v( b2 h5 J) \% Y! l
/ F0 q9 e! H4 n; N5 a. W & {$ q. e# s0 E* x) h
8 ^" T# z) H' R/ Y5 B8 F5 V1 M
' ` N. | u5 s! J( {3 c9 v
6 d& y1 a5 l$ Q; h
31wyoo5uc3v640361616.jpg
; d0 _# J0 x% r% K: V
0 o, z" n" U9 b$ I! e, O 很大的一个坑,稍有不注意,又是一次硬件研发失误!
) _' A2 d( l" m- a; X6 |. G
% `1 e' \# B% j% I
$ A$ G; R2 V; r/ l+ m! E, o # Y. |, c) M3 K3 ^, F
4 N; ~( @6 m0 A% _ 1 C6 Q5 m5 J* R' R; Y( j
; B2 l: _3 n" o; _+ W
+ ~0 }6 B5 D: R- h7 y3 K 3 `+ e* S) c2 |; z
! m+ O6 [- T) P, `
$ \: z. M( B' P" V: G \5 h" g* K8 b
y5qv5zkox5v640361716.jpg
' {9 G Y' } g: O7 W
" X2 e( @$ s H* K7 E% k 嵌入式 C 语言函数的返回值,也有其应用“潜规则”?, K/ M: y# I k2 q3 R( Z$ p# c
3 X+ ?8 y, i- U! _" Q* I0 r0 H2 l- t
/ K0 n q5 f$ I2 y" _
& k, J/ J# w7 `9 w) s " Y Z H B2 ^
0 w- a% z6 _" P0 T7 e
我是老温,一名热爱学习的嵌入式工程师- v2 [- R8 I5 q2 s/ q0 u
关注我,一起变得更加优秀! |
|