|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在学习单片机的时候才真正知道C语言是什么它是来干什么的~但是C语言用到嵌入式只是它小小的一部分他的应用还有很多地方呢,呵呵我们这里就不讨论这个了。我们是不是在写程序的时候错误很多就算编译通过了也达不到我们预期的结果,完了自己是不是也很难找到错在哪儿吧?我绝得语言之所以能称之为语言,它肯定是一种工具一种相互交流相互通信相互传达之间的意图的工具,作为语言那肯定得有自己的语法,要想相互交流肯定得先学好它的语法吧(比如像表达式,函数,循环,指针)我称之为C语言的语法。C语言虽然很强大但是他也有不少陷阱,所以我发这篇博客有两个目的一是:把C语言一些误用易错的地方总结一下,二是把C语言一些基本语法总结一下~0 }1 B; J8 _. N
第一次:1 X; H2 b$ O+ h7 R5 S' L, T
1.关于自增自减(即++i,i++)
7 c/ \4 T7 |9 f要想给一个数加一或减一我们可以:
; |4 U. u2 W+ i T/ x3 f% mi += 1;
( k$ W& x8 D$ Oj -= 1;9 V+ S) D8 C2 z9 Q: L9 \
4 ^" ~+ Q4 e; M4 K h! C而C语言还允许用++和--运算符,其实这里有误导,因为++和--可以作为前缀和后缀,这样的话他们就有可能改变操作数的值,下面让我们来看看:
/ j4 i2 p8 ^% x" ~: U8 ei = 1;$ R# v$ [ S% T$ p
printf("i is %d\n",++i); /* prints i is 2 */
; _8 b! B2 r+ b# p$ u2 [8 cprintf("i is %d\n",i); /* prints i is 2 */
4 J' ~- d% \4 s3 `( z/ k计算表达式i++的结果是i,但是会引发i随后进行自增:
% I' W: L. c/ ~/ p( ji = 1;
* M' o" U: C; S. N7 V R( ?printf("i is %d\n",i++); /* prints i is 1/ */
- Y, g1 q0 \' f, jprintf("i is %d\n",i); /* prints i is 2 */1 [3 S9 m( `4 H: Q. \7 ~# V
第一个printf 显示了i自增前的原始值,第二个printf显示了i变化后的新值;当然 -- 类似我就不举例了~. I, G; f! i! |2 }5 v' u
但在同一个表达式中多次使用++和--往往很难理解我们看看下面的例子:
- @3 N3 @# ]2 xi = 1;
0 E/ c( B+ B9 Mj = 2;+ }/ L( @0 f7 `6 d7 H% \, o
k = ++i + j++;/ u, N- P4 E0 ]: T- @) O$ J
i,j,k最终值分别是2,3,4而++i是2 j++是2;
6 l; m5 ]; |% ~( j s W总结:不管是++i还是i++执行这条语句后i的值都加一了只是(++i)的值加一了而(i++)没变,4 l% @7 G* k- |
2.typedef与#define
8 ^1 ^$ k: x& C( y; J/ [* S2.1.typedef& H* o( l0 E2 J8 s
C语言除了直接使用标准的类型名(如 int char float double)和自己声明的结构体、共用体、指针、枚举类型外,还可以用typedef声明新的类型名来代替现有的类型名。- L3 J9 V: w* X7 `/ q
typedef unsigned char u8;
+ _7 W$ x0 S# P( b* Ctypedef unsigned int u16;
1 D5 w, T) N# `* B4 Ru8 count;
/ f1 E* j7 z! Q7 Du16 time;! B0 e2 `" t4 u- X9 `
typedef struct
3 y% `# l% O, t& i+ h4 O5 k{6 _& M. j* H' U, c' h
u8 month;
/ \$ K, v8 n2 C2 d' mu8 day;
+ F* t) {! a' ?8 o2 cu16 year;- c. I3 {8 p1 T) ]
}DATE;
6 l6 x/ C' K GDATE brithday;3 ]! P4 x# T9 `9 k: M7 n- i$ ?
总结一下,声明新的类型名的方法:
- s" x# F) L5 t- a1.先按定义变量的方法写出定义体(如 unsigned int i)
; t! E4 x$ C. ]4 w) S2.在变量名换成新的变量名(如将 i换成u16). H1 l: \9 V2 U( |
3.在最前面加上typedef (typedef unsigned int u16)# P* ~# v5 P1 {8 e* y8 U
4.然后用新类型名去定义变量
2 X6 O6 H3 R: m* I5 u2.2 #define, r# L4 P: ~9 H! c; P0 x
2.1.1不带参数的宏定义
6 u% z" R+ `, [2 c( O% Q#define 标识符 字符串
# N5 d( U) q3 Y2 `& `" o6 b9 U7 M( H#define PI 3.1415926( t9 [& C# b: E/ z. a; }5 b9 c
注意:
/ R" }( p. L( f9 X2 c& |* M1.它的作用是在本程序中用指定的标识符PI来代替3.1415926
/ }+ x' C- S9 s% y2.宏定义是用宏来代替字符串也就是做简单的置换,不做正确性检查如果写成 X( G1 r- a* D# @, Y3 w0 E
#define PI 3.l4l6926% u' ~6 r/ E' u) ]& i i
即把1写成了字母l但是预处理照常代入不做任何语法检查!!- ~/ s ?5 B0 I- E
2.1.2带参数的宏定义6 v0 C$ O. Y! H1 G+ A' E: f
#define 宏名(参数) 字符串
6 m' ]1 P+ O/ q' G2 n+ F2 X#define S(a,b) a*b
( l$ O; `, U4 {- a) ~. Jarea = S(a,b);! y5 u: n, Z4 c c
#define MAX(x,y) (x)>(y) ? (x) y)
. V+ u' ?. R; I/ v3.typedef和#define的区别
, F: S- A. w% x; y一般来说typedef 因为它能正确处理指针类型
9 e1 e$ Z$ M2 B% j+ S* b; s" Jtypedef char *String1;
5 W5 U% B( P" I* @- S+ i, |, ]. [+ a#define String2 char *
( J' G2 E3 f: gString1 s1,s2;
`5 ?2 ?' p" I! h: c2 RString2 s3,s4;
/ o* W. G/ } b' H* Zs1,s2,s3 被定义为了char* 但s4却被定义为了char型* E! |. |; ^7 z# n u0 i
3. static 变量
" f$ M; T; c4 P+ d4 a" estatic变量大致分为三种用法1 N9 f( B- ?: l7 w8 ^3 j( _+ J$ K
1. 用于局部变量中,成为静态局部变量. 静态局部变量有两个用法,记忆功能和全局生存期.
" j6 S0 w& `7 b6 l- E" d7 N2. 用于全局变量,主要作用是限制此全局变量被其他的文件调用.0 J7 K: T4 W0 c' q9 M1 s9 t
3. 用于类中的成员.表示这个成员是属于这个类但是不属于类中任意特定对象
, _( I S7 E1 x7 c) T6 N1. 静态局部变量! K7 k' I4 V% E3 U" o a$ d; j1 |
静态局部变量属于静态存储方式,它具有以下特点:& [" J% N! q( b# e. K# Z5 w9 G( y! i
(1)静态局部变量在函数内定义 它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
; K- ^& X2 s% F. c* L(2)允许对构造类静态局部量赋初值 例如数组,若未赋以初值,则由系统自动赋以0值。
3 D9 T I9 g% U; `! b(3) 对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。* {$ A. `# P9 o2 V3 A6 i7 t' C
举例如下:+ D3 u% X) r4 m4 s2 v2 w
void fun()/ V- I" ?5 n% x* d6 p& y
{' q M4 K# w% x g2 K
static int a = 1;" S( t; P! d" d" A& i
a++;9 g. \* _) b/ a6 a. K4 C
}
" @6 g2 x" R) H( K- d/ k& B0 o在第一次进入这个函数的时候,变量a被初始化为1!并接着自增1,以后每次进入该函数,a就不会被再次初始化了,仅进行自增1的操作;在static发明前,要达到同样的功能,则只能使用全局变量:; `/ B" o& n& ]7 X; G; ]6 J
int a = 1;
$ N6 Q3 D! f0 xvoid fun()5 @: M6 t( y' ~+ x q; }
{- i" g d( v% v5 C6 D* x8 I; f, }
a++;
1 o1 W8 f2 i# t# q5 b6 z4 V8 z* M; w}7 n5 i9 z# d) p
2.静态全局变量# _5 Z: X m. o
全局变量(外部变量)的之前再加上static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于,非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。
; o7 J; W y0 \3.static的类成员变量
: p3 b5 n2 l" w7 p+ Ystatic关键字有两种意思,你看上下文来判断7 b, m* d$ E6 ?) ~6 Z* g) D8 X
1.表示变量是静态存储变量,表示变量存放在静态存储区.
2 ~) N, j1 ~! H* z2 x; a0 e, Q4 \2.
! m$ n, E0 C' l. W7 P表示该变量是内部连接(这种情况是指该变量不在任何{}之内,就象全局变量那样,这时候加上static),也就是说在其它的.cpp文件中,该变量是不可见的(你不能用).
- g: o6 v* L. C2 {# J, V- [- c) j4 n二、static 函数 —— 内部函数和外部函数# |3 R. @8 u7 K; F* q3 W
当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。" O0 ]+ Q" |' p) P/ t
1 内部函数(又称静态函数)
. M3 l* `0 b1 m9 G- p* g如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。
3 T. o' E7 x$ b7 j" @定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:* I$ A$ l4 d) [( U) u- ]
static 函数类型 函数名(函数参数表): i+ Z; d0 `: E% M( C& H
{……}
, z5 Z7 `/ Q8 L9 e/ H# ~" W关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。
/ s7 `/ m4 W# i- r* j& |7 i使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。
$ Y- a7 C7 H2 ~9 p8 G8 |2 外部函数3 N# Z0 V0 x9 T) i
外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
5 w% l! l+ Z/ p8 {( I' w[extern] 函数类型 函数名(函数参数表)/ s# t) v: U9 V5 N; P8 \7 g
{……}
- L0 @6 A/ G% R$ Z! @% J5 E0 h调用外部函数时,需要对其进行说明:) M. H; h9 S( a7 z; j. H
[extern] 函数类型 函数名(参数类型表)[,函数名2(参数类型表2)……];$ j/ s. J* o' o Z. _- N- w2 }7 ^5 M
# F) u! W8 o/ u2 _
|
|