嵌入式C语言实战开发(五)

时间:2021-10-11 19:12:03

一、关键字

1、register关键字

(1)register作用的实现原理?

计算机的三大组成部分:

 嵌入式C语言实战开发(五)

CPU的三大组成部分:运算器、控制器、存储器

          cache高速缓存器

             

存储器

          寄存器

 

2)为什么要把变量变为寄存器变量?(register关键字作用?)

经常被访问的变量我们就可以用register修饰为寄存器变量,请求编译器尽可能的将变量存在CPU的内部寄存器中,节省了CPU从内存中抓取数据的时间,从而提高了运行效率。

3)使用register关键字应注意哪些?

register只能修饰局部变量,不能修饰全局变量和函数;

register修饰的变量不能通过取地址来获取寄存器变量;

register修饰的变量一定是CPU能接受的数据类型。

2、static关键字

(1)static关键字作用:

static修饰的变量全部保存在数据段的静态存储区,没有初始化的系统默认初始化为0

static修饰静态局部变量,延长局部变量的生命周期,直至整个程序结束后释放。

static修饰全局变量,使得此全局变量只在本文件可见,不能在其他文件调用。

static修饰函数,使得此函数只在本文件调用,不能在其他文件调用。

(2)什么时候用static关键字?

    可以重命名变量 、函数;

想让局部变量在程序结束后释放用static,我们要尽可能的少的使用全局变量,全局                    变量比较危险,对所有文件可见,可能在别的文件中发生更改。

想让全局变量、函数只在本文件中被调用,对函数、全局变量起保护作用。

 

3、extern关键字

extern关键字:(全局变量)外部声明,声明这个变量是在其他文件中定义的,声明时一定要标注变量的数据类型。

 

4、const关键字

const修饰变量,使变量成为只读变量。

比如:const int num = 5;

const 修饰num,num为只读变量,空间里的值可变,但是不能通过num这个变量名来修改这个空间的对应值。

举例:

(1)const int a = 10;

(2)int const a = 10;

(3)const int a[10] = {1,2,3,4,5,6,7,8,9,10};

(4)const int *p;

(5)int * const p;

(6)const struct devices dev[5];

(7)struct devices const * dev[5];

技巧:

将类型去掉;

const修饰谁,谁的值就是不能更改的,是readonly.

(1)去掉类型int变成了”const a = 10”,a的值就不能变了;

(2)去掉类型int变成了”const a = 10”,a的值就不能变了;

(3)去掉类型int变成了”const a[10]”,a数组里的值就不变;

(4)const修饰*p,去掉类型int变成了”const *P”,p所指向的空间的值就不变;

(5)const 修饰p,去掉类型int *变成”const p”,指针p里的值就不能改变,也就是说p不能再指向其他地址,但是p所指向的空间里的值是可变的;

(6)去掉类型struct devices变成”const dev[5]”,dev[5]数组里的值就不改变;

(7)这是一个devices结构体类型的指针数组,它就拥有5devices结构体类型指针,每个指针指向一个devices结构体,const修饰*dev[5],去掉类型struct devices变成”const *dev[5]”,指针数组dev中每个元素指向的空间里的值不变。

const 使用注意事项:

(1)const修饰变量,一定要对变量做初始化;

(2)一般用const修饰函数形参,防止函数实现过程中修改形参的值。

 

5、typedef关键字

给数据类型重起名字;

作用:

(1)提高代码的移植性;

(2)方便在编程定义变量、方便编写程序;

(3)解释某些变量的作用,起到注释的作用。

拓展:

typedefdefine区别:

1、执行时间:typedef在编译阶段有类型检查的功能;define是宏定义,在预处理阶段,只进行简单机械的字符串替换,不进行任何检查;

2、功能:typedef定义类型的别名,定义与平台无关的数据类型,与struct结合使用等等;define不止可以为类型起别名,还可以定义常量、变量、编译开关等;

3、作用域:typedef有自己的作用域;define没有作用域的限制,只要是之前定义过的宏,在以后的程序中都可以使用;

 

6、volatile关键字

(1)不会在两个操作之间把volatile变量缓存在寄存器当中,在多任务中,甚至stejmp环境下变量可能被其他程序改变,编译器无法知道,volatile就是告诉编译器这种情况的;

(2)不做常量合并,常量传播等优化;

(3)对volatile变量的读写不会被优化掉,如果你对一个变量赋值,但后面没用到,编译器常常可以忽略掉那个赋值操作,然而对Memory Mapped IO处理是不能这样优化的。

 

二、复合数据类型

1、struct结构体

作用:封装数据

#include<stdio.h>

 

struct student

{

    int id;

    char name[20];

    int age;

};

 

typedef struct student Stu;

 

int main()

{    

    struct student stu;

    

    struct student *p_stu = &stu;

 

   // p_stu->id = 1;

 

    scanf("%d",&(p_stu->id));

    printf("id = %d\n",p_stu->id);

 

    strcpy(p_stu->name,"zhangsan");

      

    printf("name = %s\n",p_stu->name);

 

    return 0;

}

 

结构体数组:

#include<stdio.h>

 

struct student

{

    int id;

    char name[20];

    int age;

};

 

int main()

{

    struct student stu_array[3];

 

    int i;

    

    for(i = 0; i < 3; i++)

    {

        scanf("%d",&(stu_array[i].id));

scanf("%s",stu_array[i].name);

scanf("%d",&(stu_array[i].age));

    }

    

    for(i = 0; i < 3; i++)

    {

        printf("%d\t",stu_array[i].id);

printf("%s\t",stu_array[i].name);

printf("%d\t",stu_array[i].age);

printf("\n");

    }

 

    return 0;

}

结构体注意事项:

结构体有字对齐、半字对齐

内存空间:相同类型放一起,减少内存空间

 

函数返回多个值:结构体封装or传出参数

 

2、union共用体

(1)与结构体区别:

 嵌入式C语言实战开发(五)

 嵌入式C语言实战开发(五)

 

 

共用体共用同一个空间,由最大的决定

 嵌入式C语言实战开发(五)

嵌入式C语言实战开发(五)

 

共用一个空间会产生值得覆盖;

 

应用:利用共用体测试操作系统是大端字节序还是小端字节序

嵌入式C语言实战开发(五)

 

#include<stdio.h>

 

union judge_cpu

{

    int num;

    char ch;

};

 

int main()

{

    union judge_cpu jcp;

   

    jcp.num = 0x12345678;

    

    int num = 0x12345678;

    char *p = #

    #if 0

    if(jcp.ch == 0x78)

    {

        printf("small!\n");

    }

    else

    {

        printf("big!\n");

    }

    #endif

    if(*p == 0x78)

    {

        printf("small\n");

    }

    else

    {

        printf("big\n");

    }

 

 

    return 0;

}

 

3、enum枚举

 嵌入式C语言实战开发(五)

嵌入式C语言实战开发(五)

 

枚举的作用:

1、杜绝幻数,提高可读性;

2、需要大量的整数宏时,用枚举。

 

三、编译预处理

1、宏定义指令

宏定义主要避免幻数,因为数字本身没有意义,宏定义命名时要清晰的表明其用途

拓展:

#define宏定义与枚举的区别:

(1)#define宏常量是在预编译阶段进行简单的替换,枚举常量则是在编译的时候确定其值;

(2)一般在编译器里,可以调试枚举常量,但是不能调试宏常量;

(3)枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个。

 

定义宏函数:

#include<stdio.h>

#define MAX(a,b) a > b ? a : b

 

int main()

{

int num = MAX(5,6);

 

return 0;

}

 

自定义函数与宏函数有什么区别:

(1)函数式宏定义省去了函数的调用、函数的返回、释放、传参等,提高运行效率。

(2)函数式宏定义不做语法检查不安全;

(3)函数安全,会做语法检查,但运行效率会低;

 

用编译时间换内存空间:宏函数

用内存空间换执行/运行时间:inline(修饰函数、内敛函数)

 

 

内置宏定义:

__LINE__打印所在行

__func__显示函数名

__TIME__显示时间

__DATA__显示日期

 

2、条件编译

#if 0   #endif

#if 1  #endif

 

#ifdef#ifndef:防止头文件包含带来的重复定义

比如:a.h

#ifndef A_H

#define A_H

...

#endif

 

3、嵌入式中的死循环

(1)while(1)

     {

     }

(2)for(;;)

     {

     }

 

break :结束所有循环

continue:结束当次循环