1、结构体
1.1概念
c语言的基本类型包括整型(int,long,….)、浮点型(flaot,double),字符型(char)等。但是在实际应用中只有这些数据类型是不够的,某些变量可能需要其中的几种一起来修饰某个变量,例如一个学生的信息就需要学号(字符数组)、姓名(字符数组)、年龄(整型)、成绩(浮点型)等,这些数据类型都不同但是他们又是表示一个整体,此时就需要一个新的数据类型——结构体,将不同类型的数据存放在一起,作为一个整体进行处理。
结构体的语句格式如下:
struct 结构体名 { 成员列表(可以是基本的数据类型,指针,数组或其他结构类型) }; |
例如:
struct book { char title[MAXTITL]; //一个字符数组表示的titile 题目 char author[MAXAUTL]; //一个字符数组表示的author作者 float value; //一个浮点型表示的value价格 }; //注意分号不能少,这也相当于一条语句 |
这个定义只是描述了一个组成这类对象的元素,但是并没有创建一个实际的数据对象,因此有时候将结构体定义叫做模板,因为它勾勒出数据该如何存储,并没有实例化数据对象。
- 首先使用关键字struct,表示接下来是一个结构体;
- 紧跟着是一个结构体名(book),这个是可选的,可以使用结构体名来创建数据对象,如:struct book library;//把library设为一个可以使用book结构体类型的变量
- 接下来就是一个花括号,括起了结构体成员列表变量,使用声明方式来描述,用分号来结束描述。其中每个成员可以使用任何一种c数据结构甚至是其他的结构体;
- 在结束花括号后的分号表示结构体设计定义的结束。
1.2 结构体变量的声明/定义
- 结构体类型定义和变量声明分开
- 结构体类型定义的同时声明变量
这种情况,变量是一次性的。
- 使用typedef创建类型别名
1.3 结构体变量的对齐存储
一般来说,我们用.的方式来访问结构体元素时,不需要考虑结构体的元素对齐,因为编译器会帮我们处理这个细节。但是做嵌入式开发经常需要从内存角度来处理结构体及其中的元素,因此还是需要掌握结构体对齐规则。
结构体的对齐访问主要原因是为了配合硬件,也就是说硬件本身有物理上的限制。比如内存每次访问时按照4字节对齐访问,那么效率是最高的;如果不对齐则访问效率要低很多。对齐访问牺牲了内存空间,换取了速度性能;而非对齐访问牺牲了访问速度性能,换取了内存空间的完全利用。
地址分布如下所示:
结构体的大小往往遵循着结构体的对齐规则:
|
编译器的默认对齐方式是4,若想修改对齐方式可以使用如下指令:
#pragma pack(n) //设置编辑器按照n个字节对齐,n可以取值1,2,4,8,16
1.4 结构体变量的初始化
结构体变量的初始化与初始化数组类似,都是使用花括号括起来,用逗号分隔开初始化好的项目列表,注意每个初始化项目必须要和结构体成员类型相匹配。
struct book s1={ //对结构体初始化 "yuwen", //title为字符数组 "guojiajiaoyun", //author为字符数组 22.5 //value为flaot型 }; //要对应起来,用逗号分隔开来, |
注意:如果在定义结构体变量的时候没有初始化,那么后面就不能全部一起初始化了。可以将一个结构体变量作为一个整体赋值给另一相同类型的结构体变量。
总结:除了“相同类型的结构体变量可以相互整体赋值”外,其他情况下,不能整体引用,只能对各个成员分别引用。
1.5 结构体变量成员的访问
用结构成员运算符点(.)可以访问结构体成员,形式为:
结构体变量名.成员名; |
如果其成员本身又是一种结构体类型,那么可以通过若干个成员运算符,一级一级的找到最低一级成员再对其进行操作。
结构体变量名.成员.子成员………最低一级子成员; |
1.6 struct声明的作用域
struct声明的位置是有作用域的,如果将声明放在函数的外面,那么该声明后面的所有函数都可以使用该结构体。如果这种声明在某个函数的内部,则只能在内部使用,并且在其声明之后才可以使用。
- 函数内部声明结构体并定义变量
- 函数外部声明结构体并定义变量
- 函数内部声明结构体,其他函数定义结构体变量
1.7 结构体数组的初始化及成员访问
前面的例子中定义了一个book类型的结构体,每本书就需要用一个book类型的结构体变量来描述,若是要描述两本书,需要使用两个这样的变量,依次类推;因此要使用一个该结构体的数组来表示这些图书,也就有了结构体数组的出现。
与普通的数组声明一样, Book bookAaary1 [3]声明library为一个具有3个元素的数组,并且每个元素都Book类型的结构。 注意bookAaary1本身不是结构体名而是一个数组名。
1.8 结构体指针变量
通用形式为:
struct 结构体名 * 指针名 |
例如struct book * pBook;
这个声明创建了一个指针类型的pBook指针变量,他可以指向任何存在的book类型的结构体。结构体指针的成员访问方式有两种:
- 使用运算符->
- 使用运算符.
由于&取地址,*取值,因此 当pBook =&library[0]时,(*pBook)=library[0],故 library[0].value 等价于 (* pBook).value;注意必须使用圆括号(优先级问题)。
总结 ->只用于结构体指针访问成员; .只用于结构体名访问成员; |
2、控制语句
控制语句用来实现对程序流程的选择、循环、转向和返回等进行控制。C语言中共有9种控制语句,可以分为三类:“条件判断语句”、“循环执行语句”和“转向语句”。
分类 | 对应语句 |
条件判断语句 | if语句 |
switch语句 | |
循环语句 | do while语句 |
while语句 | |
for语句 | |
转向语句 | break语句 |
continu语句 | |
return语句 | |
goto语句 |
2.1 条件判断语句
2.1.1 if语句
if语句有3种形式:
- if语句
通用形式为:
if(expression) statement; |
如果对expression求值为真(非0),则执行statement;否则,跳过statement。statement可以是一条简单语句,或者是一条花括号{}括起来的复合语句。
- if else语句
通用形式:
if(expression) statement1; else statement2; |
如果对expression求值为真(非0),则执行statement1;否则执行else后面的statement2。statement1和statement2可以是一条简单语句,或者是一条花括号{}括起来的复合语句。
如果要在if和else之间执行多条语句,必须用花括号{}把这些语句括起来成为一个块。
- 多重选择else if
通用形式:
if(expression) statement1; else if(expression) statement2; …………….. else statement_n; |
当程序中有许多if和else时,若没有花括号{},则编译器将else与最近的if匹配,例如如下代码段。
当number=5、10、15输出分别时什么呢?
2.1.2 switch语句
通用形式为:
switch(表达式) { case 常量表达式1:语句1; case 常量表达式2:语句2; …. case 常量表达式n:语句n; default:语句n+1; } |
前缀case 和 default 本身并不改变控制流程,它们只起标号作用。在执行上一个case标志的语句后,继续顺序执行下一个case 前缀所标志的语句,除非上一个语句中最后用break语句是控制转出switch结构。如果没有匹配的case标签,程序则转至标有default的语句(如果有的话);否则将转至执行紧跟在switch语句后面的语句。
注意:
- default 标号是可选的;
- case标号后的语句是可选的;
- case 后面的常量必须互不相同。
2.2 循环语句
2.2.1 while语句
通用形式为
while(表达式) 语句块 |
while语句先计算“表达式”的值,当值为真(非0)时,执行“语句块”;执行完“语句块”,再次计算表达式的值,如果为真,继续执行“语句块”……这个过程会一直重复,直到表达式的值为假(0),就退出循环,执行 while 后面的代码。
2.2.2 do{}while语句
通用形式为
do 语句块 while(表达式); |
do-while循环语句与while循环语句的不同在于:它会先执行“语句块”,然后再判断表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while 循环语句至少要执行一次“语句块”。在嵌入式软件开发中,若一段循环有多个出口且至少需要执行一次循环体时,可以采用do-while语句和break相结合。
2.2.3 for语句
通用形式为
for(表达式1;表达式2;表达式3) 语句
|
流程图如图:
- (1) 执行表达式1。
- (2) 判断表达式2是否为真(非0为真,0为假)。
- (3) 若为真,则执行步骤(4),否则,执行步骤(7)。
- (4) 执行循环语句。
- (5) 执行表达式3。
- (6) 程序流程转到步骤(2)。
- (7) for循环之后的语句。
-
2.3 转向语句
以一段输出阵列的代码来说明转向语句,正常输出如下所示。
2.3.1 continue语句
continue语句用于3种循环语句中,执行到该语句时,会跳出本次迭代的剩余部分,并开始下一轮迭代。如果continue语句在嵌套循环内,则只会影响包含该语句的内层循环。
2.3.2 break语句
break语句用于3种循环语句和switch语句中,当程序执行到循环中的break时,会终止包含它的循环,并继续执行下一阶段。
2.3.3 return语句
使用return语句的另一个作用是中止函数并把控制返回给主调函数的下一条语句。
2.3.4 goto语句
goto语句的形式为:
goto 标签名 |
标签的命名遵循变量命名规则。在c语言中我们基本上不用,因此不多介绍。