找回密码
 注册
关于网站域名变更的通知
查看: 231|回复: 1
打印 上一主题 下一主题

嵌入式C语言常用关键字

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-5-20 14:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
1.static关键字; A6 D' ~9 k8 ?
这个关键字前面也有提到,它的作用是强大的。
6 j) G. u" u1 R" Z. B, n要对static关键字深入了解,首先需要掌握标准C程序的组成。
- l) Q% z0 b, m$ C% C标准C程序一直由下列部分组成:
4 ~& A, {6 G/ y) S0 v! d       1)正文段——CPU执行的机器指令部分,也就是你的程序。一个程序只有一个副本;只读,这是为了防止程序由于意外事故而修改自身指令;
8 X. q1 U: N3 M# _% x       2)初始化数据段(数据段)——在程序中所有赋了初值的全局变量,存放在这里。/ I. S9 P% b' m1 U: D6 z+ _
       3)非初始化数据段(bss段)——在程序中没有初始化的全局变量;内核将此段初始化为0。
4 b- G( f0 q' {注意:只有全局变量被分配到数据段中。9 _& _/ M3 [# X9 K7 H; I/ s( z
       4)栈——增长方向:自顶向下增长;自动变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。这句很关键,常常有笔试题会问到什么东西放到栈里面就足以说明。
% o$ i. g+ C! W' T       5)堆——动态存储分配。/ ?, e( }9 B5 X6 K

; A! ^- W7 R. [  ^/ {嵌入式C语言当中,它有三个作用:
+ ^( _- ~4 y7 W0 D作用一:在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
  d, T/ e. E: ~2 s3 _0 _: A$ v这样定义的变量称为局部静态变量:在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。也就是上面的作用一中提到的在函数体内定义的变量。除了类型符外,若不加其它关键字修饰,默认都是局部变量。比如以下代码:9 I: m& C& K1 D  H/ g
void test1(void)
# w* t" a5 }/ ~, v{
  [6 i9 S7 T/ {9 v" g    unsigned char a;
' z0 k& C" Y  D+ {    static unsigned char b;
$ l  X9 C# w5 e: Y    …' E* w( R; k, ~3 S4 q& Z3 k
    a++;
  d8 j' G* y  X' u- r    b++;
. }" J( v" F* B* n7 j# l+ l9 u}
8 ]* K, U2 |4 G8 {& d在这个例子中,变量a是局部变量,变量b为局部静态变量。作用一说明了局部静态变量b的特性:在函数体,一个被声明为静态的变量(也就是局部静态变量)在这一函数被调用过程中维持其值不变。这句话什么意思呢?若是连续两次调用上面的函数test1:* h. L5 f4 _  Q  x& Y. ~
    void main(void)1 r$ `  t2 `# D/ N0 j
    {0 o$ `& o* k3 Q# [
       …: C$ U( J7 v4 ]% [( d: i* F9 O
       test1();; c7 Y2 W+ G3 Q/ A8 f
       test1();. c& T/ [( O7 t5 Y' o( I
       …: r. i+ G% S3 t
    }
8 @% F7 r4 P! E* }1 W; f! M然后使程序暂停下来,读取a和b的值,你会发现,a=1,b=2。怎么回事呢,每次调用test1函数,局部变量a都会重新初始化为0x00;然后执行a++;而局部静态变量在调用过程中却能维持其值不变。! T) s  g1 q* `1 b
通常利用这个特性可以统计一个函数被调用的次数。. D5 Z0 U0 g* Q2 x" h7 k
声明函数的一个局部变量,并设为static类型,作为一个计数器,这样函数每次被调用的时候就可以进行计数。这是统计函数被调用次数的最好的办法,因为这个变量是和函数息息相关的,而函数可能在多个不同的地方被调用,所以从调用者的角度来统计比较困难。代码如下:
8 v! N7 d8 h( o0 z' J$ M1 b  h7 J$ N+ I6 i/ ~- x  s
" W8 v& Y4 W( f, j$ t
void count();+ }6 K# H- ~  O) m( |% \- t
int main()
8 n9 d' s8 E7 t9 D9 t* C* F{+ @' n1 x5 Z6 `& U- j
    int i;2 H2 W* A5 V) J  W; \- v8 ~9 q
    for (i = 1; i <= 3; i++)
. W2 U5 h) A% H# K* b    {
' n0 i9 c4 U3 H( C& ~! d        count();
" K$ Q7 }4 F+ e4 X# I1 W    {
6 }5 `) S- `" w8 P     return 0;. w( g2 t' @0 [2 \7 a
}
& e  e- y: }" Uvoid count()
' D5 z& V8 u' t8 X{$ d2 @& g8 ^6 P( `% _& `. e
    static num = 0;
" L0 l( r' ^( f, q. k! D8 \, e    num++;
" d, ]2 F9 [1 L5 y# ^    printf(" I have been called %d",num,"times/n");
6 j9 ]1 ]' _8 _3 _9 B( z  w}
! g0 w! a% p  e3 l8 V' m/ m$ g5 n输出结果为:
9 ?1 K  _( p/ Q" y1 P/ S* WI have been called 1 times.
3 x, j5 [$ @( F" E/ _* ?' t- zI have been called 2 times.: ^# z0 m- Z; i: L5 U- w+ w5 l+ e
I have been called 3 times.6 r4 H8 D1 V2 f# h- y4 P% v2 P. O
5 B, \/ F+ D! t* Z' {$ J& o
看一下局部静态变量的详细特性,注意它的作用域。
4 L% L& }- T3 H* K9 |+ t: }0 D" ? 1)内存中的位置:静态存储区
0 o" o' _& h: B- ~) A6 q. E1 e   2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)6 m, X; q- P! _+ h
   3)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。
0 w" c: j9 [! b+ P, @5 \& x   注:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。, k; x2 D5 r5 J
' I0 v6 N( I/ A1 @! T/ W
作用二:在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。2 N+ z; b: r+ n- e
这样定义的变量也称为全局静态变量:在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。也就是上述作用二中提到的在模块内(但在函数体外)声明的静态变量。
2 Q* j3 N! L( K$ G) e$ G定义全局静态变量的好处:6 h& Y' M, T- h
<1>不会被其他文件所访问,修改,是一个本地的局部变量。
1 y# i+ i4 J3 m6 I<2>其他文件中可以使用相同名字的变量,不会发生冲突。
! R! h( q6 Q# o) \* Y% L' f6 l5 A全局变量的详细特性,注意作用域,可以和局部静态变量相比较:
# R5 D# a, G1 w1)内存中的位置:静态存储区(静态存储区在整个程序运行期间都存在)
3 Q6 o+ h: b3 S- ]0 Y    2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
0 x) U# N8 @- J( F4 x% l, j4 p    3)作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。. O+ s- |% K( x( r" Z% N& q3 t- p
当static用来修饰全局变量的时候,它就改变了全局变量的作用域(在声明他的文件之外是不可见的),但是没有改变它的存放位置,还是在静态存储区中。' F. Q% J  S4 B  `+ v( O' s: I

* b8 s/ [3 D. f& T4 q8 \作用三:在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。6 ?1 j) ?  j, D: W% `# ^# Q& u8 f
) ^% n& ^$ a" ^9 A8 e+ u
这样定义的函数也成为静态函数:在函数的返回类型前加上关键字static,函数就被定义成为静态函数。函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。3 u( S0 z4 r7 }; Y
定义静态函数的好处:
$ v- {1 t; T$ I. F7 k4 L: I: e<1> 其他文件中可以定义相同名字的函数,不会发生冲突
3 }) }4 x* F$ I. l( V+ b3 [<2> 静态函数不能被其他文件所用。它定义一个本地的函数。
& d7 l" Q. q: ?& J  ?# a这里我一直强调数据和函数的本地化,这对于程序的结构甚至优化都有巨大的好处,更大的作用是,本地化的数据和函数能给人传递很多有用的信息,能约束数据和函数的作用范围。在C++的对象和类中非常注重的私有和公共数据/函数其实就是本地和全局数据/函数的扩展,这也从侧面反应了本地化数据/函数的优势。
5 W3 l. h8 ]& B
# F$ M* O% o$ F5 l/ r最后说一下存储说明符,在标准C语言中,存储说明符有以下几类:5 Q/ U4 Z; K8 [! J( Q" _8 [) X
auto、register、extern和static
" B: ?" m. U9 t& }1 @% D) d: Z7 I对应两种存储期:自动存储期和静态存储期。
) K7 ~' x  N- _6 I" X. Kauto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。$ v2 h5 q0 N8 l0 U/ U
关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。+ J& Y( l5 H  s6 K/ d
2. const 关键字
2 f, {. r6 n  A# Z8 D2 r; J' ~const关键字也是一个优秀程序中经常用到的关键字。关键字const 的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。合理地使用关键字const 可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。( Z' i* S% g+ N" N6 z- S
深入理解const关键字,你必须知道:: A5 ~4 c; ]$ X3 v
a. const关键字修饰的变量可以认为有只读属性,但它绝不与常量划等号。如下代码:0 K4 d. B4 S8 ]3 H/ a
const int i=5;; y- ~( K8 E+ ~. n
       int j=0;
" U2 a& l& U0 Q7 a  Z7 D* o7 W" `       ...
1 B8 U  S: O) e+ E; t       i=j;   //非法,导致编译错误,因为只能被读6 c0 X" T  ], K5 B
       j=i;   //合法
! ^+ C# {( `* h! [* Db. const关键字修饰的变量在声明时必须进行初始化。如下代码:" ], |9 g& Q6 A3 ^& E* }
const int i=5;    //合法. L4 e$ V% _" k3 J4 q, c* G. ~! ?
     const int j;      //非法,导致编译错误# M6 O: a4 @3 ]; |3 z# W9 P& l
c. 用const声明的变量虽然增加了分配空间,但是可以保证类型安全。const最初是从C++变化得来的,它可以替代define来定义常量。在旧版本(标准前)的c中,如果想建立一个常量,必须使用预处理器:
9 B: f9 F/ h. B% j                #define PI 3.141591 k- x; W' r7 H: N( p. M
此后无论在何处使用PI,都会被预处理器以3.14159替代。编译器不对PI进行类型检查,也就是说可以不受限制的建立宏并用它来替代值,如果使用不慎,很可能由预处理引入错误,这些错误往往很难发现。而且,我们也不能得到PI的地址(即不能向PI传递指针和引用)。const的出现,比较好的解决了上述问题。3 d* S( n+ Z1 ~0 q: }
d. C标准中,const定义的常量是全局的。
1 \/ r$ M* B& B; Ze. 必须明白下面语句的含义,我自己是反复记忆了许久才记住,方法是:若是想定义一个只读属性的指针,那么关键字const要放到‘* ’后面。
( B% A8 d+ F  vchar *const cp; //指针不可改变,但指向的内容可以改变1 c# v: @+ w1 ^& [
char const *pc1; //指针可以改变,但指向的内容不能改变
* h$ L; X: ~6 p5 i1 rconst char *pc2; //同上(后两个声明是等同的)
. r% Y7 o! i% I/ M$ M       f. 将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。
0 y9 }0 ^4 c+ c! K       参数const通常用于参数为指针或引用的情况,且只能修饰输入参数;若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const修饰。例子:
+ ~" T2 T9 B: U$ Uvoid fun0(const  int * a );
  ~3 p+ d. R9 `) Qvoid fun1(const  int & a);: e5 k3 i# ]' y: I# k
调用函数的时候,用相应的变量初始化const常量,则在函数体中,按照const所修饰的部分进行常量化,如形参为const int * a,则不能对传递进来的指针所指向的内容进行改变,保护了原指针所指向的内容;如形参为const int & a,则不能对传递进来的引用对象进行改变,保护了原对象的属性。# s) z  i" a9 _3 v; y2 L
       g. 修饰函数返回值,可以阻止用户修改返回值。(在嵌入式C中一般不用,主要用于C++)$ B( O! o: e$ C2 N; w) ~0 e2 U
       h. const消除了预处理器的值替代的不良影响,并且提供了良好的类型检查形式和安全性,在可能的地方尽可能的使用const对我们的编程有很大的帮助,前提是:你对const有了足够的理解。9 }' F& n$ x2 T: G& }/ ^
       最后,举两个常用的标准C库函数声明,它们都是使用const的典范。
/ \9 M- `6 \, p- E: A( \       1.字符串拷贝函数:char *strcpy(char *strDest,const char *strSrc);4 V3 Z) a) @' x  R
       2.返回字符串长度函数:int strlen(const char *str);1 j3 q' Y" n; S6 r& b* h6 O; L

3 `/ Z6 K  G$ |' h' A3. volatile关键字
  i: Y5 L: t+ P3 |1 R) W+ D1 _一个定义为volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。/ |4 j% N% e& q. H- \8 D" t3 r. d
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如: 2 i' b  B6 t, z( O! ?" G
static int i=0;
1 D' f' Q$ Z1 s" C* G& \
7 O* f; O9 i; u
* U& y- g# k1 ~& w# ~- |) uint main(void) ) _1 f) z: o3 O" K+ n/ G2 Z; N# G
{ 3 j# g$ W( y, h# w
    ...
8 `4 w" ?2 T' _( I' R0 Y    while (1)
7 [" ?9 c; v  Q% J2 m    { 7 O/ L  K1 A3 ?- |3 e* J7 m
        if (i)
# a* i' b- R6 D* T: f$ k            dosomething(); 0 l- b+ _# j/ Z6 y
    } ! b+ o5 C) M6 Y; w3 v
} - u) s$ l& n4 H- }; j# k- I

( k7 e8 F( L4 Z. P
  ~, i9 M. Z. X2 T7 v! c# P% Y/* Interrupt service routine. */
  Q- ]: R6 T8 o: pvoid ISR_2(void) 6 K5 K# a" f8 g- k' @7 p0 T
{
, S) P6 ?. Z9 m/ a: q. S* A     i=1;
: h9 h7 I6 t$ a- j} % j( p  B3 s# e3 y! Q+ ]: V
- h. m1 N# a2 X  _! v

: i+ ^8 c" E% v5 i       程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。 6 h/ u/ K5 h4 W( s6 s: [7 T& S2 q5 H
! F# @$ g0 B/ {2 c4 z  M$ d9 _

& v( V8 R0 ]. B. \! z/ u+ }如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
+ a' D/ R9 R4 F" K' y: f  K) Y, Z9 N$ e

7 x" l0 y3 ]/ V, M5 ~$ [一般说来,volatile用在如下的几个地方: $ [2 \8 n. M6 W! X+ I: ^9 R& v6 x
1 |5 L" A$ N& q$ @
  Y0 v' P, a; X1 ?& n6 K1 |7 t
1、中断服务程序中修改的供其它程序检测的变量需要加volatile; 2 F$ v, H  [) V4 m6 \4 K* {
# Z* y( v' j1 }, U# Q) s7 H
$ A& j0 K4 z+ {( `: A
2、多任务环境下各任务间共享的标志应该加volatile; 8 t- b# I' t1 \7 Q. g& V

" W0 Z/ P8 d& w" k& n* P9 o& B' Q% i# G5 J
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
& M; _! A; O/ o不懂得volatile 的内容将会带来灾难,这也是区分C语言和嵌入式C语言程序员的一个关键因素。为强调volatile的重要性,再次举例分析:/ y; a# L# @( R* k$ c, W  n
代码一:                              
- w3 f+ N+ u6 |6 G2 v6 [  x) _/ Yint a,b,c;                             0 }' _) @: ?0 |5 S
//读取I/O空间0x100端口的内容   
! G6 V: l. c' l/ i3 p: n9 n4 Na= inword(0x100);                                                  0 |' S4 h1 O9 c7 y" H
b=a;                               % Q/ x/ Q# Y1 r/ A5 R6 ~5 D% ?
a=inword(0x100)                            . j+ W7 [$ I( d$ z% q
c=a;  
1 c) r8 K7 e: q8 q- Z$ d: l! y代码二:   4 W  g2 L# ]1 q. D) [
  volatile int a;   
$ W; L. ^8 w0 o9 I1 C  int a,b,c;                             
! {/ h2 ^- Z9 d$ p( U//读取I/O空间0x100端口的内容   
: s2 n+ ?; D6 va= inword(0x100);                                               
6 i* V/ H* t3 n8 Z; Kb=a;                               $ a: U' P; L6 g$ [( N
a=inword(0x100)                        ; m* E, ]9 Y0 u" M" x
c=a;
% F- q5 J2 I7 h. n. t在上述例子中,代码一会被绝大多数编译器优化为如下代码:3 k! u9 x! x' N( t0 i" s
       a=inword(0x100)1 m' F, c& C6 H, E" K3 @( f
       b=a;  J, `- ~$ @7 L- S7 o9 F! l$ m! l
       c=a;; `6 L* i3 g9 P0 J  |' r
这显然与编写者的目的不相符,会出现I/O空间0x100端口漏读现象,若是增加volatile,像代码二所示的那样,优化器将不会优化掉任何代码.
5 G) A4 ]2 w- k/ g! _+ K: E       从上面来看,volatile关键字是会降低编译器优化力度的,但它保证了程序的正确性,所以在适合的地方使用关键字volatile是件考验编程功底的事情.0 \2 L" |; O* A4 Z
: v1 i8 j  L- u- g+ X- ~$ ^4 h
4.struct与typedef关键字
6 r$ R) [4 I8 q/ v* n3 v" Z2 z2 s面对一个人的大型C/C++程序时,只看其对struct的使用情况我们就可以对其编写者的编程经验进行评估。因为一个大型的C/C++程序,势必要涉及一些(甚至大量)进行数据组合的结构体,这些结构体可以将原本意义属于一个整体的数据组合在一起。从某种程度上来说,会不会用struct,怎样用struct是区别一个开发人员是否具备丰富开发经历的标志。: U7 }! R; h! U; C
  在网络协议、通信控制、嵌入式系统的C/C++编程中,我们经常要传送的不是简单的字节流(char型数组),而是多种数据组合起来的一个整体,其表现形式是一个结构体。' d9 @; _( E! K
经验不足的开发人员往往将所有需要传送的内容依顺序保存在char型数组中,通过指针偏移的方法传送网络报文等信息。这样做编程复杂,易出错,而且一旦控制方式及通信协议有所变化,程序就要进行非常细致的修改。
8 q( U4 j$ R: Y! l7 A5 r4 {8 n用法:
& i- m8 I+ z# P, u在C中定义一个结构体类型要用typedef:
: a+ k: M" ]$ D8 j9 Q5 Otypedef struct Student& O4 Z; \( I8 G: `0 U( U/ D6 y! Q
{
+ h* y' ~' T( l2 v8 D& w       int a;
* M; E, V% w9 t1 v. j5 i}Stu;- R! [/ X4 E2 X) _3 x
于是在声明变量的时候就可:Stu stu1;
# H/ p- T7 H- B7 J" C如果没有typedef就必须用struct Student stu1;来声明
* k! x$ K& ~( S4 E6 l这里的Stu实际上就是struct Student的别名。* t. z2 T" ]0 d! C8 o
另外这里也可以不写Student(于是也不能struct Student stu1;了)
0 i' U5 V: B" k+ x" Btypedef struct9 H, Y' `* g  s
{
" W2 X3 I3 z  X5 c' K       int a;
8 r# W+ I. l+ D' I% J" }7 B! ~}Stu;
. c1 ~( X$ s# m; ystruct关键字的一个总要作用是它可以实现对数据的封装,有一点点类似与C++的对象,可以将一些分散的特性对象化,这在编写某些复杂程序时提供很大的方便性.
; H+ s, P( b7 C: X. e/ F! H比如编写一个菜单程序,你要知道本级菜单的菜单索引号、焦点在屏上是第几项、显示第一项对应的菜单条目索引、菜单文本内容、子菜单索引、当前菜单执行的功能操作。若是对上述条目单独操作,那么程序的复杂程度将会大到不可想象,若是菜单层数少些还容易实现,一旦菜单层数超出四层,呃~我就没法形容了。若是有编写过菜单程序的朋友或许理解很深。这时候结构体struct就开始显现它的威力了:+ @! \+ n7 t; m, M) N$ d
//结构体定义2 y- q2 E) R% g/ P3 X; L0 u
typedef struct" w  [( P2 v: _5 g( w; B
{: r6 y+ w9 V/ I# E/ H
unsigned char CurrentPanel;//本级菜单的菜单索引号, |) ~7 z" }* ~) |( ]9 G
unsigned char ItemStartDisplay; //显示第一项对应的菜单条目索引0 C  A) Z  m  e6 ?9 l! w
unsigned char FocusLine;  //焦点在屏上是第几项! v& z' e* R# E% e
}Menu_Statestruct;9 w4 C: y8 N$ k

5 @- G& {$ a% c; {7 t6 ?typedef struct
. }; N4 K7 s- v$ V( h' K' ?' r. u{
) x1 Q' `/ s" _# x9 hunsigned char *MenuTxt; //菜单文本内容
, M9 c0 g- ~9 w6 b( Z9 s+ Cunsigned char MenuChildID;//子菜单索引
$ R7 h$ m7 x& }8 I3 i: U4 t( Wvoid    (*CurrentOperate)();//当前菜单执行的功能操作
$ |9 f$ A7 o: q; q}MenuItemStruct;
- d' {1 n1 |6 f4 Z ) k( Y2 o! s1 n0 x9 U
typedef struct% l% p' \1 q6 E0 Z
{
; l% ~! k1 Q0 G! @/ BMenuItemStruct  *MenuPanelItem;- S  T* P2 U) W
unsigned char    MenuItEMCount;5 b) i* d% m- Z+ S4 G; ^, O2 M
}MenuPanelStruct;
- S3 U" ~5 o" ^! \+ J9 |; {/ g ' n' P8 V( Q: T7 h0 q$ m

该用户从未签到

2#
发表于 2020-5-20 15:21 | 只看该作者
在嵌入式C语言当中,它有三个作用
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-26 00:28 , Processed in 0.156250 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表