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

嵌入式C语言常用关键字

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
1.static关键字6 r9 @6 l' x% j: b- I
这个关键字前面也有提到,它的作用是强大的。
7 K4 S) w8 Q; y. O1 o要对static关键字深入了解,首先需要掌握标准C程序的组成。
: W, H0 j% O  i5 F7 K标准C程序一直由下列部分组成:9 p  T6 l( @4 p( Y- w; R$ x
       1)正文段——CPU执行的机器指令部分,也就是你的程序。一个程序只有一个副本;只读,这是为了防止程序由于意外事故而修改自身指令;
! V2 s, A0 ~- m# l6 X+ \       2)初始化数据段(数据段)——在程序中所有赋了初值的全局变量,存放在这里。
; @# U  H2 j+ l; X" ~8 \       3)非初始化数据段(bss段)——在程序中没有初始化的全局变量;内核将此段初始化为0。
/ D$ h+ h) p/ r. Q" t* X& o8 b/ K' F注意:只有全局变量被分配到数据段中。5 a2 m' [% u2 `
       4)栈——增长方向:自顶向下增长;自动变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。这句很关键,常常有笔试题会问到什么东西放到栈里面就足以说明。/ v1 X; E0 n* b# W" w  c
       5)堆——动态存储分配。
1 c8 f3 w$ z' y. F1 x7 q( p9 p6 L
) c: X0 `  A: ?7 h嵌入式C语言当中,它有三个作用:
" v2 w/ ~3 m+ e( g作用一:在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2 n; s2 j" O" K. f/ ~+ S1 U) u这样定义的变量称为局部静态变量:在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。也就是上面的作用一中提到的在函数体内定义的变量。除了类型符外,若不加其它关键字修饰,默认都是局部变量。比如以下代码:
" s  q  h+ n% _void test1(void)
. I' B0 G4 p8 R. E- @, `{$ m: }3 ~* D/ @( B
    unsigned char a;
( v1 n0 a# M" f    static unsigned char b;
1 U; j1 [1 V, c9 `$ ~% X2 C9 v    …( N# ~! f/ U5 p* g
    a++;8 Q$ I$ a+ U: l8 |
    b++;
- x3 _- l6 E, m1 J4 u}
8 Q2 q: A! O! C; k# C- H: j- s在这个例子中,变量a是局部变量,变量b为局部静态变量。作用一说明了局部静态变量b的特性:在函数体,一个被声明为静态的变量(也就是局部静态变量)在这一函数被调用过程中维持其值不变。这句话什么意思呢?若是连续两次调用上面的函数test1:# J! [# I0 e3 k  G  ^
    void main(void)  `4 G8 j5 N+ t- W, H8 o( U
    {
4 P8 P) o) a5 {! J/ J2 z( o       …& u9 E+ V! X; H8 d- i% ^
       test1();
4 C2 M* f( f1 I& ^, ?       test1();
$ L. k9 _$ ^3 L: O5 G       …
6 [5 H4 R4 U. d$ j    }
% V$ C: g- [. o4 r然后使程序暂停下来,读取a和b的值,你会发现,a=1,b=2。怎么回事呢,每次调用test1函数,局部变量a都会重新初始化为0x00;然后执行a++;而局部静态变量在调用过程中却能维持其值不变。
1 s% A; I2 `0 Q; P/ |9 g通常利用这个特性可以统计一个函数被调用的次数。
- |; p4 c9 Q* q3 L( j. |声明函数的一个局部变量,并设为static类型,作为一个计数器,这样函数每次被调用的时候就可以进行计数。这是统计函数被调用次数的最好的办法,因为这个变量是和函数息息相关的,而函数可能在多个不同的地方被调用,所以从调用者的角度来统计比较困难。代码如下:
! J* Q. A+ s7 D- G
/ W& U) e! s& d% ^- m9 p7 g# A3 ^% u3 p- n; c4 c( R0 ]
void count();
" v; z# q; ?6 cint main()8 ~& L7 g/ C6 U4 c7 s% T
{7 U* S  {/ Y' x1 _
    int i;: J7 ?4 R# ^" m: b9 T! W1 ]
    for (i = 1; i <= 3; i++)
- X0 m& Q6 `! I8 N" H/ _    {
% E/ _* F* B( e1 L        count();
: C) \) D# ]; v  D/ {  P+ _    {
0 L; f: E" C4 |% j9 r4 y     return 0;5 ?" e: q7 }. b- X$ S
}
& Q9 u3 P; M9 }( x( zvoid count()8 z, W6 @. I1 M
{
' ]9 Y+ P5 _: ?; P+ x- f    static num = 0;# l7 a7 _0 K6 S0 _# `8 m, x% o( W- k0 ^
    num++;; o5 D  {8 `; i
    printf(" I have been called %d",num,"times/n");- D! F" [$ T+ ~4 L, g
}
5 p) f8 B' u, _$ d7 m$ ~' A输出结果为:
9 r9 Q) L$ U5 U9 y0 l# mI have been called 1 times.# s3 k/ t& K# X4 J' Q5 W
I have been called 2 times." q; b1 z1 |5 ~, }+ t+ h, }
I have been called 3 times.
+ M9 y+ l9 j6 u+ T7 e; Z+ j- _; k% R
$ i& c; H5 f! a0 K( O- l& D/ L& u看一下局部静态变量的详细特性,注意它的作用域。# M/ `4 x% N4 n( Q  g4 E& R
1)内存中的位置:静态存储区7 j( f3 M6 E! @5 y# J+ n" n
   2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)  O0 k3 Y1 B  T% \! |! A) f
   3)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。- S* C* [2 u: j- T; l( R; V
   注:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。
: b0 R* I) l0 C, }. R8 W
. L# F1 A% x, J- {# A作用二:在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
% t2 C7 |. g) V1 M这样定义的变量也称为全局静态变量:在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。也就是上述作用二中提到的在模块内(但在函数体外)声明的静态变量。
" P! f2 Y: F/ M& I9 I9 G定义全局静态变量的好处:5 l6 q$ B/ _) s' }: E- X$ F7 p
<1>不会被其他文件所访问,修改,是一个本地的局部变量。
  L. T) i! v9 G' j# J6 W* B<2>其他文件中可以使用相同名字的变量,不会发生冲突。
) r2 x- w# f7 V8 |4 j- t全局变量的详细特性,注意作用域,可以和局部静态变量相比较:
; Z2 M1 T4 A0 r1)内存中的位置:静态存储区(静态存储区在整个程序运行期间都存在)
. \3 Q* V) q) a6 i3 V3 M    2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
1 y) {8 s7 K1 v' o3 ]9 S    3)作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。
, Q# ^! D6 v* E; r, o; R7 I, [% _当static用来修饰全局变量的时候,它就改变了全局变量的作用域(在声明他的文件之外是不可见的),但是没有改变它的存放位置,还是在静态存储区中。
) ~$ Z2 ]+ z* @8 q
! N! h+ m# P# ?* b作用三:在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。. \( v2 v7 r5 P. i

7 ^! D5 c3 y$ U* J这样定义的函数也成为静态函数:在函数的返回类型前加上关键字static,函数就被定义成为静态函数。函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。
! m! k6 N; {: g0 j, t6 g. w% Q5 F定义静态函数的好处:
2 W7 F6 H+ O6 @; S+ ?  d& `<1> 其他文件中可以定义相同名字的函数,不会发生冲突
# q3 W1 i( P+ F9 g<2> 静态函数不能被其他文件所用。它定义一个本地的函数。
# b9 s  z* D5 k" Y这里我一直强调数据和函数的本地化,这对于程序的结构甚至优化都有巨大的好处,更大的作用是,本地化的数据和函数能给人传递很多有用的信息,能约束数据和函数的作用范围。在C++的对象和类中非常注重的私有和公共数据/函数其实就是本地和全局数据/函数的扩展,这也从侧面反应了本地化数据/函数的优势。
, ~1 G9 F' T. G$ b 2 f* {  X- X3 M; O& j' [0 ^
最后说一下存储说明符,在标准C语言中,存储说明符有以下几类:0 E/ _$ a. J0 _6 w% i1 W  H- S
auto、register、extern和static/ p5 M7 n  |/ [$ u
对应两种存储期:自动存储期和静态存储期。
, O$ v9 N: V! v" H* }auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。
- s4 j" {/ w' g关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。
( \6 K' x; Y; }8 V( v0 y9 S8 Y; N2. const 关键字
+ k" p! [5 M  f5 p% r+ Oconst关键字也是一个优秀程序中经常用到的关键字。关键字const 的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。合理地使用关键字const 可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
  V( G1 c: L  _2 W) `深入理解const关键字,你必须知道:( f& d6 h( A5 q% k
a. const关键字修饰的变量可以认为有只读属性,但它绝不与常量划等号。如下代码:
% z8 A. a+ S7 t7 v8 Oconst int i=5;
" T4 i1 S% G! [/ {( K       int j=0;3 g; o( Y  Q5 Z1 f
       ...2 ]9 _1 Q; T5 J  b  K
       i=j;   //非法,导致编译错误,因为只能被读
# a+ W8 J) k6 ]       j=i;   //合法4 s- Y  i. j- \  ~& `
b. const关键字修饰的变量在声明时必须进行初始化。如下代码:' ~- o, \8 i+ \! Q/ U7 I; s
const int i=5;    //合法1 f  I$ ~4 h& O2 t$ Q
     const int j;      //非法,导致编译错误
4 y. n0 ^, f; l* Cc. 用const声明的变量虽然增加了分配空间,但是可以保证类型安全。const最初是从C++变化得来的,它可以替代define来定义常量。在旧版本(标准前)的c中,如果想建立一个常量,必须使用预处理器:
' x) h! L" M4 ?9 |8 d! T1 E* ]( l                #define PI 3.14159
0 v5 P8 b  j1 }; w9 ]此后无论在何处使用PI,都会被预处理器以3.14159替代。编译器不对PI进行类型检查,也就是说可以不受限制的建立宏并用它来替代值,如果使用不慎,很可能由预处理引入错误,这些错误往往很难发现。而且,我们也不能得到PI的地址(即不能向PI传递指针和引用)。const的出现,比较好的解决了上述问题。
0 U3 P  C- d( ?d. C标准中,const定义的常量是全局的。+ t4 n, w# ?9 l  G
e. 必须明白下面语句的含义,我自己是反复记忆了许久才记住,方法是:若是想定义一个只读属性的指针,那么关键字const要放到‘* ’后面。
7 H. a9 z2 P3 M0 R$ _3 Ychar *const cp; //指针不可改变,但指向的内容可以改变
4 g6 a9 p+ _" B$ w! nchar const *pc1; //指针可以改变,但指向的内容不能改变5 o. A% B1 a1 J. W9 w
const char *pc2; //同上(后两个声明是等同的)
0 f/ o; U/ I5 w9 Y6 a4 f       f. 将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。. z/ R, A  k6 T
       参数const通常用于参数为指针或引用的情况,且只能修饰输入参数;若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const修饰。例子:
1 i1 e% s! O8 G) f$ ]; D$ bvoid fun0(const  int * a );1 U6 W: E2 P# ^) D- k
void fun1(const  int & a);9 i5 r3 z% w5 b5 t7 i4 G
调用函数的时候,用相应的变量初始化const常量,则在函数体中,按照const所修饰的部分进行常量化,如形参为const int * a,则不能对传递进来的指针所指向的内容进行改变,保护了原指针所指向的内容;如形参为const int & a,则不能对传递进来的引用对象进行改变,保护了原对象的属性。
/ a) v, y0 }- X4 h       g. 修饰函数返回值,可以阻止用户修改返回值。(在嵌入式C中一般不用,主要用于C++)
. `! B" c" @0 Q       h. const消除了预处理器的值替代的不良影响,并且提供了良好的类型检查形式和安全性,在可能的地方尽可能的使用const对我们的编程有很大的帮助,前提是:你对const有了足够的理解。
; }0 |6 p: a! z# @% |, |) @       最后,举两个常用的标准C库函数声明,它们都是使用const的典范。( x8 E7 t6 J. I4 T% a. F! k
       1.字符串拷贝函数:char *strcpy(char *strDest,const char *strSrc);7 w' L0 G% ^: }% f
       2.返回字符串长度函数:int strlen(const char *str);
6 @# s5 r+ x2 T
# @) V5 v2 e$ i1 f7 G+ J9 @8 a3. volatile关键字. Y! @5 H1 y7 H+ @
一个定义为volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。+ A- ?  K, o! F5 V4 o7 ?. N
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
; U- i% P- U# S% h$ R/ L! Ystatic int i=0;
" E( _0 G* _; J
0 t" C# z% ~4 s; p" ?! l! A5 E; M5 L/ `/ y
int main(void) 4 K% E) L2 E$ M# x" c8 }0 d' h! {
{
$ Y* }. k# Z! H: m    ...
* k2 e* ^. c4 U0 a% w5 }9 P    while (1) ( S: e- {" V2 @( z" e' u, T1 c' K6 ~
    { 8 v, q9 A6 a; T" o+ z
        if (i)
, ^' m' O" Y! k5 S7 D2 C' [- _' C            dosomething();
# W: \* W8 B" u6 u    }
( w7 X' B! b* y8 F1 `7 e( I} 4 r7 ~0 R( M6 Z
4 q" k) V3 c5 A" L/ L4 {8 c4 U$ Z
' U2 G' a4 x  l& T
/* Interrupt service routine. */
! W% Y: m! ~  m1 w! Kvoid ISR_2(void)
/ I( [- Y" t0 r6 U2 l{ 5 j2 {2 r2 K; Y5 u! f+ O, ^' Q
     i=1;
7 c) N" j, Z- ?+ T" n9 B6 D}
7 w" P& L7 B8 U9 @/ Q. X6 n2 F8 X* B" k/ _. k* z5 G
: {' q3 j$ f* u3 o+ ~5 f; d
       程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。
7 t7 y2 k: G' [3 b' M0 a5 }1 N! C0 Z, Z+ d' E8 W
5 `" t! v0 ]! g7 W
如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。 0 @& I$ z! ~$ g
2 ~( H& W  D# s" E4 A' h! ~' o9 X$ |
7 @: X8 X7 v6 I7 a
一般说来,volatile用在如下的几个地方: 6 g$ \, B+ n* j/ N" S
( L" K; f9 T6 R. \& Z2 f7 m
5 V% o7 z$ F4 L! T
1、中断服务程序中修改的供其它程序检测的变量需要加volatile; ; n0 p4 _5 J2 }& @, H

) U+ \0 A# X$ Q% s* ]: L2 C$ c! q6 _5 L
2、多任务环境下各任务间共享的标志应该加volatile;
2 q9 v: a) k0 A  m; ?$ Q: H. A1 A9 ^

; ^2 R7 [/ i' g; S  o. U3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;8 U, a+ a; e* p1 z% R$ u+ C) q
不懂得volatile 的内容将会带来灾难,这也是区分C语言和嵌入式C语言程序员的一个关键因素。为强调volatile的重要性,再次举例分析:
. G6 m4 C# l/ b5 |  Y7 q1 B代码一:                               " A. C( k" K5 I) P% E
int a,b,c;                             + ?$ T2 M" A, Q, k1 C! I' ]0 l, m
//读取I/O空间0x100端口的内容    + y, S4 x* ?- a8 c- L
a= inword(0x100);                                                  4 P# [* ~* R: K/ o" {# K0 ]5 r
b=a;                              
1 T& k6 E1 J" |) m6 s2 T: ]1 sa=inword(0x100)                            : B/ x7 z2 ~/ R2 I, q8 z
c=a;  
4 ?6 t8 p  [* l" s# @% O+ h" G; {代码二:   8 ^% u, g+ K* ^
  volatile int a;   
4 F' \  D) c6 q: m' T& Y$ K  @  int a,b,c;                             
3 \* m: l* @7 L% \+ N$ L//读取I/O空间0x100端口的内容   
. e! _4 t5 O* j. Ka= inword(0x100);                                               % c3 Q* W6 z! g+ G5 m
b=a;                               ' z8 q( t/ ~6 z6 |1 R2 i
a=inword(0x100)                        , u8 Z# d$ e5 @: Y& P, {- M
c=a; ; L& X9 ~* v* d/ v- @/ I5 v
在上述例子中,代码一会被绝大多数编译器优化为如下代码:
/ C% S6 i) _/ N$ v       a=inword(0x100); w7 j! B  S" ]2 u& ~( G1 Z3 A( Q$ W
       b=a;
" g& C7 V9 D4 _- a% e, M+ Y! ]       c=a;
; N) i2 k1 p8 O% ?/ z7 \这显然与编写者的目的不相符,会出现I/O空间0x100端口漏读现象,若是增加volatile,像代码二所示的那样,优化器将不会优化掉任何代码.
0 }2 o, p; l" f, ]" Z       从上面来看,volatile关键字是会降低编译器优化力度的,但它保证了程序的正确性,所以在适合的地方使用关键字volatile是件考验编程功底的事情.- b: s. t/ }4 y( G8 }' z/ j- p
' o! V& Q7 }5 _8 B8 Q/ m
4.struct与typedef关键字" W3 ^$ [) b) ]! X! o
面对一个人的大型C/C++程序时,只看其对struct的使用情况我们就可以对其编写者的编程经验进行评估。因为一个大型的C/C++程序,势必要涉及一些(甚至大量)进行数据组合的结构体,这些结构体可以将原本意义属于一个整体的数据组合在一起。从某种程度上来说,会不会用struct,怎样用struct是区别一个开发人员是否具备丰富开发经历的标志。
% O" d9 `8 h& n6 W+ l1 L0 q  在网络协议、通信控制、嵌入式系统的C/C++编程中,我们经常要传送的不是简单的字节流(char型数组),而是多种数据组合起来的一个整体,其表现形式是一个结构体。* i! ^* @( G4 m' a
经验不足的开发人员往往将所有需要传送的内容依顺序保存在char型数组中,通过指针偏移的方法传送网络报文等信息。这样做编程复杂,易出错,而且一旦控制方式及通信协议有所变化,程序就要进行非常细致的修改。
6 ?8 o* K! m: L- v: c% n- ?5 W用法:6 z; A' q$ V' m& `6 a
在C中定义一个结构体类型要用typedef:) P) C: w% v# D8 W  b
typedef struct Student
0 i% l% S; f* q! D9 i{
# R& Z4 a. U/ P       int a;
) P& T9 v2 h7 F- r0 o! u}Stu;
  W8 G& _  N5 u# x9 F0 D于是在声明变量的时候就可:Stu stu1;' u( A  ?% B" n1 x6 n- A7 D  K
如果没有typedef就必须用struct Student stu1;来声明( n' s% A7 g# l1 w: }# X3 E
这里的Stu实际上就是struct Student的别名。- W# Q; [: Y" `: e
另外这里也可以不写Student(于是也不能struct Student stu1;了)( u& I6 K- X/ A
typedef struct- l3 n/ x; F. @6 O9 `, e8 c7 w3 O) u
{
8 X# {3 K7 ?# p( [       int a;: H( x( p* e( o0 C
}Stu;% ?/ H3 k2 D0 H+ L
struct关键字的一个总要作用是它可以实现对数据的封装,有一点点类似与C++的对象,可以将一些分散的特性对象化,这在编写某些复杂程序时提供很大的方便性.$ _% v; ]6 \4 D$ j! U" T
比如编写一个菜单程序,你要知道本级菜单的菜单索引号、焦点在屏上是第几项、显示第一项对应的菜单条目索引、菜单文本内容、子菜单索引、当前菜单执行的功能操作。若是对上述条目单独操作,那么程序的复杂程度将会大到不可想象,若是菜单层数少些还容易实现,一旦菜单层数超出四层,呃~我就没法形容了。若是有编写过菜单程序的朋友或许理解很深。这时候结构体struct就开始显现它的威力了:
( J8 n# m: G5 m. z: P1 i' J5 v//结构体定义( n$ I: V8 c! J0 h# q/ b( o
typedef struct
8 Q6 P) u1 i+ q* O{
. N3 R; T2 O/ ]$ `  `7 T) aunsigned char CurrentPanel;//本级菜单的菜单索引号0 x& M% r. _. w8 c" z3 ~& B2 A
unsigned char ItemStartDisplay; //显示第一项对应的菜单条目索引
  @2 Z: F1 I6 T1 \+ bunsigned char FocusLine;  //焦点在屏上是第几项$ [6 M& c" l9 ^. @8 A5 }! e" [
}Menu_Statestruct;3 r' P* C( T+ Q6 w$ m1 }* `+ h: |
8 m7 \0 x( ]7 E( c5 l
typedef struct' Q+ t/ J- E( K: z; g/ R% U. {0 V
{
0 V. N3 c8 f( E( @# wunsigned char *MenuTxt; //菜单文本内容
+ x1 f! b" a* a+ B3 d* x7 j& xunsigned char MenuChildID;//子菜单索引
$ P) c( [2 `6 f& Tvoid    (*CurrentOperate)();//当前菜单执行的功能操作
0 h" A5 J8 C% a" ^; K}MenuItemStruct;5 f) U1 I' k/ Z% g2 _" ?" p5 n
1 N2 W' z0 X6 y  S2 M7 |
typedef struct0 p& V: v7 x7 ?4 v1 S. H
{$ k$ `  Q  `3 Q# |) d
MenuItemStruct  *MenuPanelItem;* l4 \) Y: h8 F$ f8 E
unsigned char    MenuItEMCount;3 e9 [: O2 I  ?1 a' G0 j' b
}MenuPanelStruct;0 W( R% K5 x/ Z: \; c* H
& I. i" K9 P9 k9 o; e/ B' z; z

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-26 05:41 , Processed in 0.171875 second(s), 25 queries , Gzip On.

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

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

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