目录
C51语法基础
1数据类型(Keil支持的)
2 运算符和表达式
C51语言的基本结构
C51函数
变量的存储类别
局部变量和全局变量
C51数组和指针
C51语言使用技巧
Keil uVision3编程技巧
一些良好的编程技巧 6
---------------------------------------
C51语法基础
1数据类型(Keil支持的)
unsigned char |
signed char |
单字节 |
unsigned int |
signed int |
双字节 |
unsigned long |
signed long |
4字节 |
float |
|
4字节 |
* |
|
1~3字节 |
bit |
位 |
0或1 |
sfr |
单字节 |
0~255 |
sfr16 |
双字节 |
0~65535 |
sbit |
位 |
0或1 |
C51扩充数据类型 补充说明:
bit 定义一个位变量,不能定义位指针和位数组。
sfr 为8位寄存器指定名称,例 sfr P1=0x90;
sfr16 和sfr相似的是用于操作SFR,所不同的是它用于操作占两个字节的SFR,比如T0和T1。当然,sfr16也可以像sfr一样用一个字节的方式访问,比如T2,可以分别以TL2和TH2进行访问。
sbit 利用它访问片内RAM中的可寻址位 或 特殊功能寄存器中的可寻址位。 比如:
sfr P1=0x90;
sbit P1_1=P1^1; // 或直接用P1.1的位地址 0x91, 即 sbit P1_1=0x91;
通常SFR使用系统预定义的就可以了。根据习惯可修改那些名字。
2 运算符和表达式
=、==、 +、-、*、/、<、<=、== 、>、>=、!=、 &&、 || 、!、 ~ 、& 、^ 、<<、 >>、复合赋值运算符……
①赋值运算符 ②算术~ ③自增自减~ ④关系~ ⑤逻辑~ ⑥位~ ⑦复合赋值~ ⑧逗号~ ⑨条件~ ⑩指针和地址~ 11强制类型转换~ 12sizeof()~
运算符优先级和结合方向 。。。
// sizeof不是函数,而是运算符,在编译时完成。
C51语言的基本结构
C51控制流:顺序、选择、循环。
选择控制语句:if-else if-else语句和switch-case语句
循环控制语句:for语名、while语句 和do...while语句
转移控制语句:break、continue、goto
C51函数
在程序设计中往往根据需要确定若干个模块,分别由一些函数来实现。而在第一阶段只设计最基本的模块,即先把架子打好,细节留待进一步的完善。
在函数声明中也可不写形参名,而只写形参的类型。编译系统会根据首部提供的信息对函数调用作正确性检查。
// 函数声明与调用的区别 。。。
函数的嵌套调用
函数独立定义,但可彼此调用。由于片内RAM数量很少,而每次调用子程序都要占用一定量的RAM资源,所以C51无法进行多层次的函数嵌套调用。
函数递归调用:
当问题的解可以通过一种解法逐次地调用问题的子集来表示时常会采用函数的递归调用。
C51递归函数声明用关键字reentrant,说明函数是可重入的。
// 标准C的函数默认是可重入的。
变量的存储类别
变量的存储从变量的作用域(即从空间)角度来分,可分为全局变量和局部变量;若从变量值存在的时间(生存期)角度来分,可分为静态存储方式和动态存储方式。
C51语言中每一个变量和函数都有数据类型和存储类别两个属性。 数据类型是指前面所谈到的字符型、整型、浮点型等;而存储类型是指数据在内存中存储的方法。存储方法分为静态存储和动态存储两大类,具体包括 自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern) 4种。
根据变量的存储类别可以知道变量的作用域和生存期。
对于目前的C51编译器,register并没有实际意义,下面介绍其它存储类别:
① auto变量, 动态分配存储空间,用完后释放。
② static变量,存储单元固定,用完后不释放。
对静态变量的说明:
l 静态局部变量在程序整个运行期间都不释放。
l 对静态局部变量是在编译时赋值的,即 只赋初值一次。 调用函数时不再重新赋初值。
l 如果定义局部变量不赋初值,则静态局部变量自动赋初值0;自动变量为随机值。
l 虽然静态变量在函数调用结束后仍然存在,但其他函数仍不能引用(不可见)。
// static 还可用于指定函数、全局变量的可见性为模块内(文件内)
③ extern变量
全局变量(在函数外部定义)的作用域为从变量的声明处开始,到本程序文件的结尾。{有时还需要用extern来声明外部变量, 以扩展外部变量的作用范围。—— 在一个源程序文件中引用另一个源程序文件中已定义的外部变量。如
一个文件中定义:int num;
另一个文件中声明引用:extern int num; // 注意,声明时类型要与定义一致}
→{ 编译原理:模块单独编译,然后通过链接器连接。如果模块A引用模块B中字义的对象,则需要使用extern声明。 例如:
文件A中定义全局变量:int gNum;
文件B中引用此变量则需要声明:extern int gNum;
文件B中声明外部对象时要确保和定义一致。安全的做法是:将多个模块共享的全局对象X定义在某模块M中,在M的头文件中使用extern声明X。
这里使用“对象”一词是因为它不只适用于变量,还适用于函数。
}
局部变量和全局变量
局部变量是在函数内定义的变量;全局变量是在函数外定义的变量。
关于变量的作用域:区别 local, module,global。
C51数组和指针
一维、"多维"
数组类型: 字符型、整型、浮点型、指针型以及结构和联合。
指针 ≠ 指针变量 它们一个是常数(地址值), 一个是变量。但人们常常混淆这两个概念。
变量的地址在形式上类似于整数,但它是指针,有类型。
下面问题有助于理解和区别它们:
指针相减运算:指向同一数组的两个指针p,q, p-q的绝对值是它们所指对象之间相差元素的个数。虽然这不同于整数相减。其相减的结果遵守 按对象类型的字节长度进行缩小 的规则。
int x; double d;
虽然&x, &d的值是整型变量x和双精度变量d的地址,但它们的类型是不同的。
在习惯上,很多情况下 指针和地址 这两个术语混用了。
和一般变量 一样,对于外部或静态指针变量在定义中若不初始化,则默认初始化为0(NULL,空指针)。
C51结构、联合和枚举
ptr_stu=(struct STU*)malloc(sizeof(struct STU));
其中,sizeof()求取STU结构的字节长度;malloc()函数分配指定字节的内存区域,返回通用指针;
枚举变量的取值:虽然枚举列表中每一项符号代表一个整数值,但不能用整数给枚举变量赋值或比较,而要按常理用枚举值。
// sizeof用法1:sizeof(变量),变量所占内存大小;
// sizeof用法2:sizeof(数组名),数组大小(而不是元素个数);
// 注意:指针也是变量,占空间4B,sizeof(指针)=4.
// typedef作用:定义类型
//EG1:typedef unsigned char unchar;
//EG2:typedef tag_STU{...}STU;
//EG2:typedef char *(*fp)(void) FP; // 函数指针,所指向的函数返回类型为char *。简写:typedef char *(*FP)(void);
//EG3:typedef void (*afp[ ])(void) AFP // 函数指针数组
C51语言使用技巧
通过一些编程上的技巧可以帮助编译器产生更好的代码。
1.使用位变量
对于某些标志位声明为位变量,节省内存。
使用位变量时应注意:
① 用"#pragra disable"说明和用"using"指定的函数,不能返回bit值。
② bit变量不能声明为指针和数组。
2.使用短型变量
3.使用无符号类型变量
这是由于51单片机不支持符号运算的缘故。 如果程序中不需要负数,可把变量声明为无符号类型的。
4.使用局部变量代替全局变量
5.避免使用浮点变量
出于效率考虑。 常用提高数值数量级和使用整型运算来消除浮点运算。
6.使用特定指针
通用指针占3字节,特定指针占2字节,同时产生的代码更紧凑高效。
7.使用“宏函数”
“函数内联”(C++术语,内联不同于带参宏,作类型检查)
8.为变量分配内部存储区
变量的存储器分配是否合理十分重要,关系到整个程序的运行速度以及代码的大小。
为提高存储速度,一般情况下存储器按照 DATA、IDATA、PDATA、XDATA的顺序使用。
另外,必须留下足够的堆栈空间。
// data/idata/pdata/xdata区别 。。。
// code用于ROM中的table定义
Keil uVision3编程技巧
用Keil编程时,首要考虑程序的可行性、可读性、可移植性等问题,这是总原则。 下面介绍一些编程技巧。
1.使用头文件定义
头文件里写的是系统函数原型和常数定义。可以自己定义头文件,比如 把一些全局变量定义在头文件中。 头文件后缀为.h,也可为.c。
→{ 头文件里写的是数据类型、常数定义,还有全局函数、全局变量声明(extern)。后缀是.h(把后缀弄成.c的做法就像男人穿胸罩御寒,有衣不穿)}
2.使用关键字预定义
从程序的可读性角度来看,使用关键字预定义可以提高编码速度和程序的可读性。
C51编译器提供关键字"_at_"为变量指定地址(绝对定址)。
一般格式如下:
[存储器类别] 数据类型 标识符名 _at_ 地址常数;
其中,“存储器类别”为data,idata,pdata, xdata等C51编译器能够识别的所有类别。若省略它,则按编译模式Large,Compact或Small等规定的默认存储器类别确定变量的存储空间。
下面是一个采用关键字"_at_"进行变量的绝对地址定位的例子:
// 同时这个例子也展示了 存储器类型与变量类型关键字的相对位置与含义
struct link{
char code *text; // 指向code区
struct link idata *next; // 指向idata区
};
idata struct link list _at_ 0x40; // 结构变量list定位于idata空间地址0x40
xdata char text[256] _at_ 0xE000; // 数组text定位于xdata空间地址0xE000
xdata int il _at_ 0x8000;
利用关键字"_at_"定义的变量称为“绝对(定址)变量”。 对该变量的操作就是对指定存储空间绝对地址的直接操作,因此不能对“绝对变量”进行初始化,对于函数和位类型变量,不能采用这种方法进行绝对地址定位。 采用关键字"_at_"所定义的绝对变量必须是全局变量。 另外 在XDATA空间定义全局变量的绝对地址时,可以在变量前加一个关键字"volatile",这样对该变量的访问就不会被C51编译器优化掉。
// C51还提供另一种绝对定寻址的方法,见absacc.h,例如XBYTE。
3.使用多文件
把一个较大的程序分成若干个源文件。
对于某些中等规模的程序,最好中用一个头文件存放程序中各部分需要共享的实体。
多文件的使用有利于程序的模块化,不同功能的程序段可以建立成不同的功能模块,这样有利于程序的重用性。
// 前面读了一点“编译原理”,最好搞清楚,否则说不定哪天你就被连接出错搞晕了。
一些良好的编程技巧
(1)重要难懂的代码要写注释,每个函数要写注释,每个全局变量要写注释,一些局部变量也要写注释。 注释写在代码的上方或者右方。也要注意,不要滥注释。
(2)变量和函数最好能按功能命名,尽量不要用简单的字母代替命名。如果函参和返回值不存在,则最好使用关键字void进行声明。
(3)常数和表格应该放到code中去,以节省RAM空间。
(4)合理使用空格
大多数二目运算符和三目运算符"?:"两边均空一格;
"(",")"运算符在其内侧空一格,但函数实现时可不用;
","后空一格;
……
(5)对齐原则
原则上关系密切的行应对齐。
长行换行尽可能在","后 或 运算符前, 并且以下各行均以该语句首行缩进,但该语句仍以首行的缩进为准,即如果下行为"{" 应与首行对齐。