目录
一、51单片机C语言要求
1、进制转换![](https://i-blog.csdnimg.cn/blog_migrate/6cba05092594f8e4280a207bd4fa5539.png)
2、C51数据类型![](https://i-blog.csdnimg.cn/blog_migrate/fb190e23d3dbaa210ca0e4908de1d3c6.png)
关键字前加static ,作用是把局部变量变成了静态局部变量,例如:
void test()
{
static int i = 0;//局部变量i被static修饰
i++;
printf("%d ",i);
}
static修饰局部变量后,使得i的生命周期变为全局变量的生命周期,直接与主程序的运行与结束挂钩。但i的作用域并没有发生变化,仍仅仅是test里。
具体解释起来:第一次调用test时,局部变量i被定义且初始化为0,打印一次,test调用结束。注意注意!因为i被static修饰,虽然test结束,但i并没有被释放。
第二次调用test时,因为此时这个i在上一次并没有被释放,所以此次并不进行定义及初始化,即int i = 0;被跳过,直接执行i++;结果自然打印2。
3、C51数据运算![](https://i-blog.csdnimg.cn/blog_migrate/0a76ddcf68553cd7c1331c7dd7f69a18.png)
(1)除和取余的区别
(2)按位左移1位和按位右移两位
(3)按位逻辑操作(异或—相同为0,不同为1)
a &= b :a与b做按位与运算,结果赋值给a;
a |= b :a与b做按位或运算,结果赋值给a;
4、C51基本语句![](https://i-blog.csdnimg.cn/blog_migrate/7f36af284924ebd51010241ed22145f2.png)
(1)for循环执行过程
5、C51数组
6、C51子函数
void Fun(int x) //x为形参
{
display(x); //x会变成6
}
void main()
{
Fun(6); //6为实参,在调用Fun函数后,6会返回到display(6)中
}
main主函数在执行Fun(6)时,会跳到Fun函数,6是我们赋予的实参,在跳到Fun函数后,会自动将我们赋予的实参赋予给形参x,在Fun函数体中调用display时,display中的x都会变成我们赋予的实参6。
int Fun(int x) //有返回值,需要将void改成返回值的数据类型
{
display(x); //x=8
return(6); //执行完函数后,会将6返回
}
void main()
{
int a;
a=Fun(8); //在执行完Fun(8)函数后,会将返回值6赋值给a
}
二、STM32C语言要求
1、C语言数据类型
其中int 和 unsigned int 在51单片机中是占16位的,在Stm32中却是占32位的,表示范围也不相同。
右边两排里,stdint关键字是指关键字在Stm32中的新名字,如char是在Stm32中的新名字,表示的意思就是8位整型数据,右边加个_t表示这是用typedef重新命名的变量类型,typedef是用来给变量类型重新命名的。uint8_t就是unsigned char 在Stm32中的新名字,表示无符号8位整型数据。
最右边一排ST关键字是ST库函数以前用的名字,在一些旧版的库函数手册里面就是用ST关键字来表示前面的数据类型,在Keil软件中也可以使用ST关键字来表示前面的数据类型。
这里建议用stdint关键字下的名字来表示数据类型,这是新版本库函数使用的方式,也是C语言stdint.h头文件里提供的官方定义。
2、宏定义
关键字:#define
用途:用一个字符串代替一个数字,便于理解,防止出错,比如我们在程序中经常用1代表高电平,0代表低电平,但是如果说1代表上拉输入,2代表下拉输入,3代表浮空输入等等,这时直接用数字来表示就会非常麻烦,那我们就可以用宏定义将数据参数映射到一个字符串上,用字符串来代替这个数字。
第二个用途是提取程序中经常出现的参数,便于快速修改,比如我们写程序里出现了10个GPIO_Pin_0,这个Pin_0是需要经常修改的,如果一个一个修改就太不方便了,这时我们就可以用一个字符串来替代GPIO_Pin_0,需要修改的时候,只需要改一下定义即可。
定义宏定义:#define ABC 12345
这个意思就是用ABC这个字符串来替代12345这个参数
引用宏定义:int a = ABC; //等效于int a = 12345
3、typedef
关键字:typedef
用途:讲一个比较长的变量类型名换个名字,便于使用
定义typedef:
typedef unsigned char uint8_t;
与宏定义的区别:
(1)宏定义的新名字ABC在左边,typedef的新名字uint8_t在右边;
(2)宏定义不需要分号,typedef后面必须加分号;
(3)宏定义任何名字都可以换,而typedef只能专门给变量类型换名字,如将unsigned char改成uint8_t;
(4)typedef定义相比于宏定义更加安全,宏定义只是改名,不管对不对,但是typedef会对命名进行检查,如果不是变量类型的名字,那是不行的,所以给变量类型命名,一般用typedef。
引用typedef:uint8_t a; //等效于unsigned char a;
这里只是增加了一个新名字,原名字也是可以继续用的。
4、结构体
关键字:struct
结构体也是一种数据类型,比如char、short、int等,这些我们可以成为基本数据类型,然后数组就是一大堆基本数据类型的集合,但它只能组合一样的数据类型,如定义char a[10]; 就是10个char型数据的组合,或int b[20]; 就是20个int型数据的组合,如果我们想组合不同类型的数据该怎么办,于是C语言的结构体就出现了。
用途:数据打包,不同类型变量的集合
在一个复杂的程序里,用结构体将一些数据打包起来,有利于我们管理或者传递这些数据,并且有利于程序员的理解。
普通字符、数组、结构体的定义与引用
int a; //定义一个int型的数据,名字叫a
a = 66; //引用方法
int b[5]; //数组的定义,定义一组数组,共5个int型数据
b[0] = 66; //数组的引用
b[1] = 77;
b[2] = 88;
数组定义如下图:
struct{char x; int y; float z;} c;
//定义结构体变量,名字叫c,结构体包含了char型的x,int型的y和float型的z三个子项
c.x = 'A'; //结构体的引用
c.y = 66; //结构体名字.子项名字 = 子项内容
c.z = 1.23;
结构体定义如下图:
由于struct定义结构体名字会很长,这时候我们可以用typedef来给struct定义换个名字。
typedef struct{ //用typedef给struct{char x, int y; float z;}换一个名字为StructName_t
char x;
int y;
float z;
} StructName_t;
StructName_t c; //StructName_t结构体的数据类型,c为结构体变量
StructName_t d;
c.x = 'A'; //结构体引用
c.y = 66;
c.z = 1.23;
结构体成员的引用方式还有一种,就是用pStructName结构体地址来引用,因为结构体是一种组合数据类型,在函数之间的数据传递中,通常用的是地址传递而不是值传递。
既然使用的是地址传递,那么子函数得到的就是结构体的首地址,这时用->这个运算符快速的引用结构体成员,
pStructName->x = 'A'; //pStructName为结构体的地址
pStructName->y =66;
pStructName->z =1.23;
5、枚举
关键字:enum
枚举跟结构体差不多,也是一种数据类型。
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;
比如我们定义一个变量,用来存储星期的值,那理论上只能取1到7的值,但是如果定义的是整形变量,那这个变量随意存什么数都行,不会收到限制,这时可能会出现数据不合法,如星期8的情况的出现,所以如果我们想要让程序更安全一些,就可以定义一个取值受限制的整型变量,这个变量就是枚举。
枚举的定义与引用
enum{Monday = 1, Tuesday = 2, Wednesday = 3} week; //枚举的定义
week的值只能取{ }里面的值,如果{ }内定义的值是按顺序累加的,那后面的赋值可以省略,比如Tuesday = 2, Wednesday = 3因为是顺序的数,可以去掉,编译器会自动填上顺序值,同样,这个变量类型名比较长,我们可以用typedef改一下名字。
//枚举的定义
//用typedef给enum{Monday = 1, Tuesday, Wednesday}换一个名字为StructName_t
typedef enum{
Monday = 1,
Tuesday,
Wednesday
} Week_t;
Week_t week; //Week_t是枚举类型,week是定义枚举变量的名字
//枚举的引用
week = Monday; //week = 1
week = Tuesday; //week = 2
week = Wednesday; //week = 3
如果在枚举引用中,写week = 8,那么编译器就会报警告,提示枚举中混入了其他变量。
6、指针
(1)指针简介
指针(Pointer)是C语言的一个重要知识点,其使用灵活、功能强大,是C语言的灵魂。
指针与底层硬件联系紧密,使用指针可操作数据的地址,实现数据的间接访问。
(2)计算机存储机制
上图为实际内存模式图,在计算机内存里,通常把内存分配成线性的区域,里面分成一个个空间,每个空间都是以一个字节为单位,如0x78就是一个字节数据,每个字节都会对应一个独一无二的地址,并且按顺序的依次往下分配地址。
我们知道,int型数据类型代表四个字节的数据,当我们定义int a = 0x12345678; 时,会如上图所示,将12 34 56 78分别装入四个字节中,先将最小的78分配到了最上面的字节,再将最大的12分配到了最下面的字节,这种分配方式叫小端模式,这样一个int型数据就对应了四个地址,如果是最大的12先分配再上面,这种分配方式就叫大端模式,但是现在大部分计算机都是采用的小端模式的分配方式。
当我们定义short b = 0x5A6B; 时,如上图所示,占两个字节,并且也是小的6B在上面,大的5A在下面。
定义数组char c[] = {0x33, 0x34, 0x35};时,就是按地址顺序依次放入内存中。
如果是占两个字节地址的数组short d[] = [0x5A6B, 0X7A8B];会先放入0x5A6B,再按小端模式分配,分配完后再分配下一个数据0x7A8B。
(3)定义指针
指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针。
定义一个指针变量:
如果电脑是16位系统,则表格中x=2;32位系统,x=4;64位系统,x=8。
(4)指针的操作
若已定义:
int a; //定义一个int型的数据
int *p; //定义一个指向int型数据的指针,p中存放的不是数据,而是地址
对指针p有如下操作方式:
char a = 0x66;
char *p;
p = &a;
printf("%x\n",a); //输出a的内容66
printf("%x\n",p); //输出指针p中存放的地址4000
printf("%x\n",*p); //*p是取指针指向的数据单元的内容,因此这里输出就是66
p++;
printf("%x\n",p); //输出4004
输出*p时,就是一种间接访问的方式,程序先访问p的内容,然后以p的内容作为一个地址,再去访问这个地址的内容,然后再输出这个地址的内容。
p++会加一个数据宽度,而int型数据宽度是4个字节,所以在p++后,输出p是地址4004。
如果a是char型,数据宽度是1,所以在p++后,输出p是地址4001,如果a是short型,数据宽度是2,所以在p++后,输出p是地址4002。
(5)数组与指针
数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。
数组的定义等效于申请内存、定义指针和初始化。
例如: char c[ ] = {0x33, 0x34, 0x35};
等效于: 申请内存
定义char *c = 0x4000;
初始化数组数据。
利用下标引用数组数据也等效于指针取内容。
例如: c[0]; 等效于:*c;
c[1]; 等效于:*(c+1);
c[2]; 等效于:*(c+2);
以上代码说明数组取下标就是指针取内容的另一种表现形式。
(6)注意事项
在对指针取内容之前,一定要确保指针指在了合法的位置上,否则将会导致程序出现不可预知的错误。
同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告。
变量可以看做0级指针,取地址&后,就是一级指针,指针再取地址,就是二级指针。
想让变量赋值给指针,就取地址&,想让指针赋值给变量,就取内容*。
int a = 66;
int *p;
p=&a; //变量赋值给指针,要加&
a=*p; //指针赋值给变量,要加*