单片机C51基础知识汇总
李小鹏
2019.12.24
目录
1、标识符
2、数据类型
3、变量
4、常量
5、二进制、八进制、十进制、十六进制
6、运算符
7、语句
8、注释
9、if语句
10、switch--case语句
11、for循环
12、while循环
13、do—while循环
14、循环控制
15、一维数组
16、二维数组
17、字符数组与字符串数组
18、函数
19、函数重入与递归
20、预处理
21、宏
22、条件编译指令
23、指针(*)
24、结构 (struct)
25、联合(union)
26、枚举(enum)
主要内容
1 .标识符
标识符就是编程时使用的表示某个事情名称的符号,如函数名、变量名、引脚名、特殊功能寄存器名等。标识符有系统标识符和用户自定义标识符之分。
标识符的命名规则:
(1) 标识符第一个字符必须是字母或下划线。
(2) 标识符只能由字母、数字和下划线三类字符组成。
(3) 标识符是区分大小写的。如 A 和 a 是两个不同的标识符。
(4) 标识符有效长度不超过 32 个字符。
(5) 标识符不能是 C51 的关键字。
2 . 数据类型
char 有符号字符型,一字节,值域-128~127。
int 有符号整型,两字节,值域-32768~32767。
long 有符号长整型,四字节,值域-2147483648~2147483647
unsigned char 无符号字符型,一字节,值域 0~255
unsigned int 无符号整型,两字节,值域 0~65535
unsigned long 无符号长整型,四字节,值域 0~4294967295
float 浮点型(都是有符号的),四字节,±1.175494E-38~±3.402823E+38
bit 位变量,一个二进制位,值域 0~1。
sbit 51 单片机特殊功能寄存器位,值域 0~1。
Sfr 51 单片机特殊功能寄存器,值域 0~255。
sfr16 51 单片机特殊功能寄存器,如 DPTR,值域 0~65535。
bit,sbit,sfr,sfr16 不是标准 C 的内容,是 51 单片机及 C51 编译器特有的,不能用指针对它们进行操作。
3 .变量
C51 规定所有变量在使用前都必须加以说明。变量说明语句由数据类型、可选的存储类型和其后的一个或多个变量名组成,形式如下:
数据类型 [存放类型] 变量表;
变量的作用范围:在花括号内说明(也称声明或定义)的变量,其作用范围仅限该花括号内,称为局部变量;在所有函数外面定义的变量,其作用范是整个程序,称为全局变量。
静态变量:在类型前加关键词 static 说明的变量,称静态变量。在函数内部定义的静态变量也是局部变量,但它在函数下次调用时,能保存上次调用的值。在函数外面定义的静态变量,是全局变量,但它只在当前 C 文件中有效。这可以防止多个 C 文件中同名冲突。
(函数加了关键词 static,可以定义成静态函数,静态函数只能被同一个 C 文件的程序调用。)
4 .常量
常量的意思就是不可改变的量,是一个常数。同变量一样,常量也可以有各种数据类型。常量可以用以下几种方式定义:
(1)宏定义
#define OFF 1 /*定义常量标识符 OFF,其值为 1*/
(2)使用 CODE 空间
char code array[]={1,2,3,4};
/*定义一个常数表,存放在程序存储器中*/
(3)常量定义关键词 const
Const int MAX = 60;
(4) enum 枚举常量
enum switchENUM {ON,OFF}; /*ON 值为 0,OFF 值为 1*/
5 . 二进制,十进制,八进制,十六进制
(1)C 语言中没有二进制的直接表示方法。
(2)以数字“0”开头的数为八进制,如 067。
(3)以“0x”或“0X”开头的数为十六进制,如 0x3F。
(4)八进制及十六进制与二进制都可以直接转换,八进制每 1 位数对应 3 位二进制数。十六进制的每 1 位对应 4 位二进制数。
(5)用四位二进制数来表示的十进制数称 BCD 码。
6 .运算符
(1 ) 赋值运算符(=)
‘=’ 赋值语句的作用是把某个常量或变量或表达式的值赋值给另一个变量。
注意:这里并不是等于的意思,只是赋值,等于用‘==’表示。
例如:
count=5;
total1=total2=0; //同时赋值给两个变量
(2) 算术运算符(+,-,*,/,%,++,--)
+ 加,单目正; - 减,单目负; * 乘法; / 除法; % 取模;
++ 自加 1(++a,先自加,再赋值;a++,先赋值,再自加)
- - 自减 1(- -a,先自减,再赋值;a- -,先赋值,再自减)
(3) 逻辑运算符(&&,||,!)
逻辑运算符是根据表达式的值来返回“真”或“假”。非 0 为真值,0 为假值。
&& 逻辑与; || 逻辑或; ! 逻辑非
(4) 关系运算符(>,<,>=,<=,==,!=)
关系运算符是对两个表达式进行比较,返回一个真、假值。
> 大于,如(4>5)的值为 0,(4>2)的值为 1。
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
这些运算符很简单,但要注意等于“==”和赋值“=”的区别,看下面的代码:
条件判断:if(a = 3) {„„}
应 该 是:if(a==3) {„„}
(5)位运算(&,|,^,~,>>,<<)
& 按位逻辑与,如 0x0f & 0x33 结果是 0x03。
| 按位逻辑或,如 0x0f | 0x33 结果是 0x3f。
^ 按位逻辑异或,如 0x0f ^ 0x33 结果是 0x3c。
~ 按位取反,如 ~0x33 结果是 0xcc。
>> 右移,移出丢去,移入补 0。如 0x33>>1 结果是 0x19。
<< 右移,移出丢去,移入补 0。如 0x33<<2 结果是 0xcc。
(6) 复合赋值运算符(+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=)
a+=b 相当于 a=a+b
a-=b 相当于 a=a-b
a*=b 相当于 a=a*b
a/=b 相当于 a=a/b
a%=b 相当于 a=a%b
a<<=b 相当于 a=a<a>>=b 相当于 a=a>>b
a&=b 相当于 a=a&b
a|=b 相当于 a=a|b
a^=b 相当于 a=a^b
(7)问号表达式(?:)
<表达式 1>?<表达式 2>:<表达式 3>
在运算中,首先对第一个表达式进行检验,如果为真,则返回表达式 2 的值;如果为假,则返回表达式 3 的值。这是一种“二选一”的表达式。
例如:
a = (b>0)? (2*3):7; //当 b>0 时,a=6;当 b 不大于 0 时,a=7。
(8)逗号运算符(,)
多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
b=1;
c=2;
d=3;
a1=(++b,c--,d+3); //选括号,再赋值,a1 取 d+3,即 a1=6
a2=++b,c--,d+3; //赋值优先级高,所以 a2 取++b,即 a2=2
(9)运算符的优先级
7 .语句
(1) 赋值语句
如:A=1+2;
T=Counter/3+5;
Area=Height*Width;
赋值不是“代数方程”,特别注意:
Num=Num+1;
//这显然不是一个等式,这是把 Num+1 的值赋给变量 Num
(2) 用逗号分隔开的声明语句
Char Area, Height, Width;
也可以把标识符写在不同的行上,如:
Vfloat Area,
Height,
Width;
这样便于给每个标识符后边加上注释。
在声明变量的时候,也可以直接给变量赋值,这叫做变量的初始化。
如:int a=3;
也可以只初始化部分变量,如:int a=3, b, c=5;
int a=3,b=a,c=5;
(3)关于 标准输入输出语句
51 单片机的 C 语言程序中可以使用控制台格式化输入、输出函数 scanf()和 printf(),但因为单片机程序使用这两个函数不是很方便,所以不做介绍。
8 .注释
(1)单行注释
一行中,在“//”后面的内容,被解释为注释。
(2 )多行注释
以“/*”开头,用“*/”结尾,即/*„„*/中间的内容被解释为注释。
9 .if 语句
(1 )形式 1
if(表达式) 语句 1;
(2 )形式 2
if(表达式)
{
语句 1;
语句 2;
„„
语句 N;
3)if—else 形式
4)if--else if—else }
if(表达式) 语句 1;
else 语句 2;
形式
if(表达式 1)
语句 1;
else if(表达式 2)
语句 2;
else if(表达式 3)
((
语句 3;
.switch--case
„„
else
语句 n;
switch(变量)
{
case 常量 1:
语句 1 或空;
case 常量 2:
语句 2 或空;
„„
case 常量 n:
10
语句 n 或空;
default:
语句 n+1 或空;
}
执行 switch 开关语句时,首选测试变量的值,并直接跳到与变量值相等的 case 常量处开始往下执行。若不与任何一个常量相等,则执行 default 后面的语句。
注意:
A.switch 中变量可以是数值,也可以是字符,但必须是整数。
B.case 的个数可以根据需要增减,也可以不使用 default。
C.每个 case 或 default 后的语句可以有很多,但不需要使用“{”和“}”括起来。
D.执行完一个 case 语句后面的程序后它并不主动跳出 switch 的花括号,而是继续往下,顺序执行。除非利用 break 来跳出。
11 .for 循环
for(<初始化>; <条件表达式>; <增量>)
{
语句组;
}
For 的执行流程是:初始化→条件表达式为“真”→语句组→增量→条件表达式为“真”→„„语句组→增量→条件表达式为“真”→语句组→增量→条件表达式为“假”→结束。
初始化是进入循环时执行的语句,通常是一个赋值语句,它用来给循环控制变量赋初值;条件表达式是一个关系表达式,它决定什么时候退出循环;“增量”可以控制循环次数,定义循环控制变量每循环一次后按什么方式变化。注意,这三个部分之间用“;”分开,而
不是“,”号。
例如:
for(i=1;i<=10;i++)
j=i*3;
上例中先给 i 赋初值 1,判断 i 是否小于等于 10,若是则执行语句 j=i*3,之后 i 值增加 1。再重新判断,直到条件为假,即 i>10 时,结束循环。
注意:
A.语句组如果是一条语句,“{”和“}”可以省略。
B.for 循环中的初始化、条件表达式和增量都是选择项,即可以缺省,但“;”不能缺省。省略了初始化,表示不对循环控制变量赋初值。省略了条件表达式,则不做判断,便成为死循环。省略了增量,则不对循环控制变量进行操作,这时可在语句组中加入修改循
环控制变量的语句。初始化、条件表达式和增量可以是对不同的变量进行测试,也可以是复合语句,以期获得特殊的循环效果。
C.for 循环可以有多层嵌套。
12 .while 循环
while(条件)
{
语句组;
}
while 循环表示当条件为真时,便执行语句。直到条件为假才结束循环。并继续执行循环程序外的后续语句。while 循环总是在循环的头部检验条件,这就意味着循环可能什么也不执行就退出。
注意:
A.在 while 循环体内也允许空语句。
B.可以有多层循环嵌套。
C.语句组如果是一条语句,“{”和“}”可以省略。
13 .do--while 循环
do {
语句块;
} while(条件);
这个循环与 while 循环的不同在于,它先执行循环中的语句,然后再判断条件是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while 循环至少要执行一次循环语句。
14 . 循环控制
(1)break 语句
break 语句通常用在循环语句和开关语句中。程序遇上 break 语句时,会跳出当前循环或 switch 语句。通常 break 语句总是与 if 语句联在一起,即满足某条件时便跳出。
注意:
A.break 语句对 if-else 的条件语句不起作用。
B.在多层循环中,一个 break 语句只向外跳一层。
C.如果有多层嵌套的循环,想从最里层跳出到最外层之外时,可用 goto 语句。
(2)continue 语句
continue 语句的作用是跳过循环本中剩余的语句而强行执行下一次循环。即结束本次循
环,根据条件进入下一轮循环。
(3)goto 语句
goto 语句是一种无条件转移语句,格式为:
goto 标号;
“标号”,函数内部用一个有效的“标识符”,后面跟一个冒号“:”,这样标识的行可以用 goto 做的跳转目标。执行 goto 语句后,程序将跳转到该标号处并执行其后的语句。标号必须与 goto 语句同处于一个函数中,但可以不在一个循环层中。通常 goto 语句与 if 条件语句连用,当满足某一条件时,程序跳到标号处运行。不提倡使用 goto 语句,它将使程序层次不清,但在多层嵌套退出时,用 goto 语句则比较合理。所有的 goto 语句
其实都是可以用break,continue 代替的。
15 .一维 数组
(1)数组就是数据类型相同的一组数,这组数可以通过“索引号”来存取。声明数组与声明变量方法相似,只要在名称后用一对方括号说明它容纳的元素个数即可:
数据类型 数组名[元素个数];
例如:
char Array1[5]; //声明一个能存储 5 个字符元素的数组,没有初值。
Int Array2[]={5,2,13,4}; //声明一个整型数组,并存入 4 个初始值。
(2)用下标访问数组元素
声明数组时,方括号中的数据是数组元素个数;存取数据时,方括号中的数据是数组元素编号(下标)。数组下标从 0 开始编号。
上面的 Array2 数组中,Array2[0]=5,Array2[1]=2,Array2[2]=13,Array2[3]=4。
若用指令:
Array2[2]=100;
则:把值 100 赋给数组 Array2 的 2 号元素(第 3 个),2 号元素原来的 13 变为 100。
若用指令:
char x= Array2[1];
则,把数组 1 号元素的值赋给变量 x,即 x=2。
16 .二 维数组
例如:
char a[3][8]; //这个数组有 3 行,8 列
char b[3][2]={ {1,2}, {4,5}, {9,0} }; //定义一个 3 行,2 列的二维数组,
上式中:b[0][0]=1; b[0][1]=2; b[1][0]=4; b[1][1]=5; b[2][0]=9; b[2][1]=0;
17 .字符数组与字符串数组
(1)字符数组
char chArray[]={’A’, ’r’, ’r’, ’a’, ’y’, ’4’};
//数组元素为字符的 ASCII 码
(2 ) 字符串数组
char strArray[]=”Array”; //将字符串“Array”共 5 个字符的 ASCII 码存入数组,并最后存入 0 值,来表示字符串结束。char strHZ[]=”单片机”; //将汉字内码存入数组,一个汉字占两个字节(两个元素),同样以 0 结束。
18 .函数
函数是 C 语言的基本单元。
(1 )普通过函数
[函数返回值类型] 函数名(参数表) [using 寄存器组号]
{
语句组;
}
(2 )中断函数
void 函数名(void) interrupt 中断号 [using 寄存器组号]
{
语句组;
}
(3 )任务函数
void 函数名(void) _task_ 任务号 [using 寄存器组号]
《单片机控制装置安装与调试》 电子工业出版社
{
语句组;
}
以上 [using 寄存器组号] 是可选项。函数与变量一样,需要先定义,再使用。如果“定义”在其它 C 文件中,或者在使用它的语句后面,则需要先进行“说明”。
(4 ) 函数参数传递
A.形式参数和实际参数
函数定义中的参数是形式参数(简称形参),函数的调用者提供给函数的参数叫实际参数(简称实参)。可以理解为,在函数调用之前,实参的值将赋值给形参,做为形参的初值。
B.形参与实参的作用范围
形参是属于函数内部的局部变量;实参是函数外部即函数调用者的变量。
C.参数传递
形参和实参可能不只一个,如果多于一个时,函数声明、调用、定义的形式都要一一对应,不仅个数要对应,参数的数据类型也要对应。
参数传递有传值和传地址之分。当函数参数为普通变量,常数时,传递的是值。如果参数是指针,数组,结构等时,传递的是它们的地址。
(5 ) 函数值的返回
如果函数定义了返回值,则可以把函数当作一个变量。函数执行完成后,就会得到一个值。
注意:
A.有返回值的函数声明函数时,函数名前面不是 void,而是返回值类型。
B.函数必须用 return 语句来返回一个值。return 语句在函数中可以不只出现一次,但是一旦遇上它,函数立即结束并返回。
C.函数的返回值可以赋给一个变量,也可以将函数放在一个表达式中参与运算。
19 . 函数重入与 递 归
(1)重入
函数正在运行,还没有结束,又被调用,称为重入。在中断函数系统、多任务操作系统编程时,容易出现这种情况。
(2)递归
递归,就是函数自己调用了自己,或者在自己函数调用的下级函数中调用自己。递归是重入的一种特殊情况。递归函数必须确保有一个终止条件来结束递归下降过程,并且返回到顶层。递归通常都可以用循环来实现,但很多时候使用递归比使用循环要简单。
20 . 预处理
预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理
指令:
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if„„#else 条件编译块
#error 停止编译并显示错误信息
21 .宏
宏定义了一个代表特定内容的标识符。预处理过程会把源代码中出现的宏标识符替换成宏定义时的值。宏最常见的用法是定义代表某个值的全局符号。宏的第二种用法是定义带参数的宏,这样的宏可以象函数一样被调用,但它是在调用语句处展开宏,并用调用时的实际参数来代替定义中的形式参数。
(1)#define 指令
#define 预处理指令是用来定义宏的。该指令神明一个标识符,然后给出这个标识符代表的代码。
例如:
01 #define MAX_NUM 10 /* 定义符号 MAX_NUM 代表 10 */
02 int array[MAX_NUM]; /* 利用符号 MAX_NUM 声明数组的容量 */
03 for(i=0;i 例如: #define ONE 1 #define TWO 2 #define THREE (ONE+TWO) //这里的括号很有必要,请看下面的例子就明白了。 如:six=THREE*TWO; 上式转换后变成:six=(ONE+TWO)*TWO; 如果没有括号,就转换成:six=ONE+TWO*TWO; //其意义与上面完全不同 宏还可以代表一个字符串常量,例如: #define VERSION Version 1.0 Copyright(c) 2003 (2)带参数的#define 指令 带参数的宏和函数调用看起来有些相似。 看一个例子:#define CUBE(x) (x)*(x)*(x) 可以用任何数字表达式甚至函数调用来代替参数 x。这里再次提醒大家注意括号的使用。宏展开后完全包含在一对括号中,而且参数也包含在括号中,这样就保证了宏和参数的完整性。看一个用法: #define NUM 3+2 volume= CUBE (NUM); 展开后为(3+2)*(3+2)*(3+2); 如果没有那些括号就变为 3+2*3+2*3+2 了。 下面的用法是不安全的: volume= CUBE (NUM ++); 如果 CUBE 是一个函数,上面的写法是可以理解的。但是,因为 CUBE 是一个宏,所以会产生副作用。这里的参数不是简单的表达式,它们将产生意想不到的结果。它们展开后是这样的: volume=( NUM ++)*( NUM ++)*( NUM ++); 很显然,结果是 10*11*12,而不是 10*10*10;可见,使用带参数的宏一定要小心。 (3)# 运算符 出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如: #define PASTE(n) adhfkj#n 则,PASTE(15) 表示 adhfkj15。 (4)## 运算符 ##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符 号。看下面的例子: #define NUM(a,b,c) a##b##c #define STR(a,b,c) a##b##c main() { printf(%d\\n, NUM(1,2,3)); printf(%s\\n, STR(aa,bb,cc)); } 最后程序的输出为: 123 aabbcc 千万别担心,除非需要或者宏的用法恰好和手头的工作相关,否则很少有程序员会知道##运算符。绝大多数程序员从来没用过它。 22 .条件编译指令 条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。 (1)#if 指令 #if 指令检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,知道出现#else、#elif 或#endif 为止;否则就不编译。 (2)#endif 指令 #endif 用于终止#if 预处理指令。 #define DEBUG 0 main() { #if DEBUG printf(Debugging\\n); #endif printf(Running\\n); } 由于程序定义 DEBUG 宏代表 0,所以#if 条件为假,不编译后面的代码直到#endif,所以程序直接输出 Running。如果去掉#define 语句,效果是一样的。 (3 )#ifdef和#ifndef #define DEBUG main() { #ifdef DEBUG printf(yes\\n); #endif #ifndef DEBUG printf(no\\n); #endif } #if defined 等价于#ifdef; #if !defined 等价于#ifndef (4)#else 指令 #else 指令用于某个#if 指令之后,当前面的#if 指令的条件不为真时,就编译#else 后面的代码。#endif 指令将中指上面的条件块。 #define DEBUG main() { #ifdef DEBUG printf(Debugging\\n); #else printf(Not debugging\\n); #endif printf(Running\\n); } (5)#elif 指令 #elif 预处理指令综合了#else 和#if 指令的作用。 #define TWO main() { #ifdef ONE printf(1\\n); #elif defined TWO printf(2\\n); #else printf(3\\n); #endif } 程序很好理解,最后输出结果是 2。 23 .指针 (* ) (1)指针的概念 邮递员是通过地址来为用户服务的,只要有地址,邮递员可以找到目标。同样,计算机中的所有数据(变量,常量,函数等)总是放在某个地方的,也总有一个“地址”,指针就是数据的地址。指针是 C 语言的精华,但是指针理解起来比较抽象,也比较复杂,这里只做简单介绍。数据有不同的类型,同样也有指向不同类型数据的指针。 指针变量的一般定义为: 类型标识符 *指针标识符; 其中指针标识符是指针变量的名字,标识符前加了*号,表示该变量是指针变量,类型标识符表示该指针变量所指向的变量的类型。一个指针变量只能指向同一种类型的变量,例如,我们不能定义一个指针变量,既能指向一字符变量,又能指向长整型变量。指针变量在定义中允许带初始化项。如: int i; //整型变量 int *ip=&i; //注意,这里是用&i 对 ip 初始化, 而不是对*ip 初始化和一般变量一样,对于外部或静态指针变量在定义中若不带初始化项,指针变量被初始化为 NULL,它的值为 0。C 中规定,当指针值为零时,指针不指向任何有效数据,有时也称指针为空指针。在使用中不要将一个整数赋给一指针变量。如: int *ip; ip=100; //错误 但是,初始化时是可以的: int *ip=100; //正确 (2) 取地址运算符(& )与取值运算符(* ) int i = 200, x; //两个普通变量 int *ip; //一个指针变量 ip = &i; //“&i”取变量 i 的“地址”赋值给指针 ip x = *ip; //取 ip 指向的地址处的内容赋值给 x *ip = 300; //将数值 300 送到指针 ip 指向的“地址”处,即 i=300 指针变量和一般变量一样,存放在它们之中的值是可以改变的,也就是说可以改变它们的指向。可以对指针进行加减运算,重新赋值等来改变其指向。通过指针访问它所指向的一个变量是以间接访问的形式进行的,所以比直接访问一个变量要费时间,而且不直观。但由于指针是变量,我们可以通过改变它们的指向,以间接访问不同的变量,这给程序员带来灵活性,也使程序代码编写得更为简洁和有效。指针变量可出现在表达式中,例如: Int x, y *px = &x; y=*px+5; //表示把 x 的内容加 5 并赋给 y (3) 指针允许的运算方式有 a. 指针在一定条件下,可进行比较。这里所说的一定条件,是指两个指针指向同一个对象才有意义。例如两个指针变量 p,q 指向同一数组,则<,>,>=,<=,==等关系运算符都能正常进行。若 p==q 为真, 则表示 p,q 指向数组的同一元素。 b. 指针和整数可进行加、减运算。指针加 1 时,表示指向下一个元素或对象,而不是简单的数值加 1。如 p 是指向某数组元素的指针,开始时指向数组的第 0 号元素,设 n 为一整数,则 p+n 就表示指向数组的第 n 号元素(下标为 n 的元素)。 c. 两个指针变量在一定条件下,可进行减法运算。设 p, q 指向同一数组,则 p-q 的绝对值表示 p 所指对象与 q 所指对象之间的元素个数。 (4)指向数组元素的指针 指针和数组有着密切的关系,任何能由数组下标完成的操作也都可用指针来实现,但程序中使用指针可使代码更紧凑、更灵活。 Int a[10], *p; //定义数组和指针变量p = &a[0]; //指针 p 指向数组 0 号元素,也可写成:“p=a;”由于数组元素在内存中是连续存放的,因此我们就可以通过指针变量 p 及其有关运算间接访问数组中的任何一个元素。如: *(p+i)=8; //表示数组下标为 i 的元素赋值为 8。 (5) 指针函数 与函数指针 当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用者,以用于需要指针或地址的表达式中。 格式: 类型说明符 *函数名(参数) 指针与可以指向函数: 类型说明符 (*函数指针)(参数) (6) 指针的指针 它们的声明有两个星号。例如: char ** cp; 当然,还可以定义指针的指针的指针。 24 .结构 (struct ) (1 )结构说明和结构变量定义 结构是由基本数据类型构成的、并用一个标识符来命名的各种变量的组合。结构中可以使用不同的数据类型。结构也是一种数据类型,可以使用结构变量。 定义结构变量的一般格式为: struct 结构名 { 类型 成员变量名; 类型 成员变量名; ... } 结构变量名; 结构名是结构的标识符不是变量名。类型为整型、浮点型、字符型、指针型和无值型。构成结构的每一个类型变量称为结构成员,它象数组的元素一样,但数组中元素是以下标来访问的,而结构是按变量名字来访问成员的。 (2) 结构变量的使用 结构是一个新的数据类型,因此结构变量也可以象其它类型的变量一样赋值、运算,不同的是结构变量以成员作为基本变量。结构成员的表示方式为(用“点”连接): 结构变量.成员名 可以把“结构变量.成员名”看成一个整体,当成一个变量来使用。 (3)结构数组 结构是一种新的数据类型,同样可以有结构数组。结构数组就是具有相同结构类型的变量集合。假如要定义一个班级 25 个同学的姓名、性别,可以定义成一个结构数组。如下所示: struct banji { char name[8]; int age; } student[25]; 结构数组成员的访问是以数组元素为结构变量的,其形式为: student[3].name[0] //第 3 号同学的姓名的第一个字节 student[3].age //第 3 号同学的性别 25 . 联合(union) 联合也是一种新的数据类型, 它是一种特殊形式的变量。联合说明和联合变量定义与结构十分相似。其形式为: union 联合名 { 数据类型 成员名; 数据类型 成员名; ... } 联合变量名; 联合表示几个变量公用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。如: union abc { int i; char k; }; 再用已说明的联合可定义联合变量。如: union abc ik; 在联合变量 ik 中,整型量 i 和字符 k 公用同一内存位置。当联合变量被说明时,编译程序自动按联合中最长的变量分配一个内在位置来存放变量值。联合访问其成员的方法与结构相同。 26 . 枚举(enum) 枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见。 例如,表示星期的 SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY 就是一个枚举。 枚举的说明与结构和联合相似, 其形式为: enum 枚举名 { 标识符[=整型常数], 标识符[=整型常数], ... 标识符[=整型常数], }枚举变量; 如果枚举没有初始化,即省掉“=整型常数”时,则从 0 开始,顺次赋给标识符 0,1,2,„„。但当枚举中的某个成员赋值后,其后的成员按依次加 1 的规则确定其值。例如, enum string{ x1, x2, x3, x4}x; // x1, x2, x3, x4 的值分别为 0, 1, 2, 3 enum string{ x1, x2=0, x3=50, x4}x; //x1=0, x2=0, x3=50, x4=51 注意: 枚举变量只能取枚举说明结构中的某个标识符常量。 27 . 类型说明 类型说明的格式为: typedef 类型 定义名; 类型说明只定义了一个数据类型的新名字,而不是定义一种新的数据类型。这里类型是任何一种数据类型定义名表示这个类型的新名字。 例如: 用下面语句定义整型数的新名字: typedef int SIGNED_INT; 使用说明后,SIGNED_INT 就成为 int 的同义词了,此时可以用 SIGNED_INT 定义整型变量。 例如: SIGNED_INT i, j; //与 int i, j 等效 typedef 同样可用来说明结构、联合以及枚举。如,说明一个结构类型: typedef struct { char name[8]; int class; } student; //这里 student 不是结构变量,而是一种结构类型 student Liuqi; //利用 student 定义结构变量 Liuqi 因篇幅问题不能全部显示,请点此查看更多更全内容