单片机C语言基础

目录

一、51单片机C语言要求

1、进制转换​编辑

2、C51数据类型​编辑

3、C51数据运算​编辑

(1)除和取余的区别                                               

(2)按位左移1位和按位右移两位

(3)按位逻辑操作(异或—相同为0,不同为1)

4、C51基本语句​编辑

5、C51数组

6、C51子函数

二、STM32C语言要求

1、C语言数据类型

2、宏定义

3、typedef

4、结构体

5、枚举

6、指针

(1)指针简介

(2)计算机存储机制

(3)定义指针

(4)指针的操作

(5)数组与指针

(6)注意事项


一、51单片机C语言要求

1、进制转换

 2、C51数据类型

关键字前加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数据运算

(1)除和取余的区别                                               

 

(2)按位左移1位和按位右移两位

(3)按位逻辑操作(异或—相同为0,不同为1)

a &= b    :a与b做按位与运算,结果赋值给a;

a |= b     :a与b做按位或运算,结果赋值给a;

4、C51基本语句

(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;        //指针赋值给变量,要加*

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

剑鞘的流苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值