枚举、结构体、全局变量、局部变量、常量总结对比

关于枚举、结构体、全局变量、局部变量、常量这些,自己经常遗忘,然后每次查找的都略微有些出入且冗余,并不能快速反应过来。这篇文章主要还是作为自己知识点的一个梳理,可能有较多错误,见谅!!

一、枚举

1.1枚举的应用

枚举的应用可以简单概括为两类

  • 用来限制 枚举变量 ,其值只能为枚举标识符中的一个。
enum {
    Monday = 1,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
} day;

通过上述枚举,可以将枚举变量day限制为周一到周日7个值。当枚举变量day = a;这个非枚举标识符时,则会报错。

  • 作为宏定义的集合,使得代码看上去更具备可读性

当代码中有大量 #define 宏定义的整数常量时,可以改用 enum 以提高可读性。

/*************************************/
// 定义一组错误代码
#define ERROR_NONE      0   // 无错误
#define ERROR_TIMEOUT   1   // 超时错误
#define ERROR_OVERFLOW  2   // 数据溢出
#define ERROR_INVALID   3   // 非法操作

/*************************************/

//使用enum后
typedef enum {
    ERROR_NONE = 0,     // 无错误
    ERROR_TIMEOUT,      // 超时错误(1)
    ERROR_OVERFLOW,     // 数据溢出(2)
    ERROR_INVALID       // 非法操作(3)
} ErrorCode;

1.2枚举使用注意事项

  • 枚举内的数据均为常量,存储在flash当中
  • 枚举内标识符,下一个标识符的值为上一个标识符+1
//情况1
typedef enum {
    ERROR_NONE,         // 无错误
    ERROR_TIMEOUT,      // 超时错误
    ERROR_OVERFLOW,     // 数据溢出
    ERROR_INVALID       // 非法操作
} ErrorCode;

//情况2
typedef enum {
    ERROR_NONE = 5,     // 无错误
    ERROR_TIMEOUT,      // 超时错误
    ERROR_OVERFLOW,     // 数据溢出
    ERROR_INVALID       // 非法操作
} ErrorCode;

//情况3
typedef enum {
    ERROR_NONE,         // 无错误
    ERROR_TIMEOUT,      // 超时错误
    ERROR_OVERFLOW = 5, // 数据溢出
    ERROR_INVALID       // 非法操作
} ErrorCode;

对于上述情况1来讲:

EEROR_NONE = 0;

ERROR_TIMEOUT = 1,

ERROR_OVERFLOW = 2,  

ERROR_INVALID    = 3;

而对于情况2来讲:

EEROR_NONE = 5;

ERROR_TIMEOUT = 6,

ERROR_OVERFLOW = 7,  

ERROR_INVALID    = 8;

对于情况3来讲:

EEROR_NONE = 0;

ERROR_TIMEOUT = 1,

ERROR_OVERFLOW = 5,  

ERROR_INVALID    = 6;

二、结构体

结构体定义时并不会分配内存,只有在创建结构体变量时才会分配内存。

struct PidController {
    int Kp, Ki, Kd;
    int err, err_last, integral;
};     //这时并不会分配内存

struct PidController pid1;  // 只有在创建结构体变量时才会分配内存

2.1关键词typedef

typedef的作用是给数据类型起别名,使得代码看上去更加简洁

 在使用结构体时,我们常常还会使用关键词typedef来使得结构体使用更加方便简洁

//不使用typedef关键词定义结构体

struct PidController {
    int Kp, Ki, Kd;
    int err, err_last, integral;
};

struct PidController pid1;  // 必须带上 struct 关键字


//使用typedef关键词定义结构体
typedef struct {
    int Kp, Ki, Kd;
    int err, err_last, integral;
} PidController;

PidController pid1;  // 直接用 PidController 作为类型,不需要写 struct 关键字

对于定义结构体来讲,其本质还是在定义一个大的数据类型——struct 结构体名,如上面代码中:struct PidController就是一个数据类型,与intunsigned intfloat并无太大的区别。

通过使用typedef可以使得 struct 结构体名 简化成 结构体名。

下面我将统一采用typedef关键词来简化

2.2不同结构体变量的不同使用方式

下面将统一以该结构体为例:

typedef struct {
    int Kp, Ki, Kd;
    int err, err_last, integral;
} PidController;

2.2.1普通结构体变量

对于普通结构体变量来讲,其定义为:

PidController pid1;

如需调用结构体内的成员:

pid1.Ki = 1;
pid1.err = 2;

2.2.2结构体指针变量

对于结构体指针变量的使用,往往需要提前定义好一个普通的结构体变量

PidController pid1; 
PidController *pPid = &pid1;  // 让指针指向这个变量

如需调用结构体内的成员:

pPid->Kp = 15;
pPid->Ki = 25;
pPid->Kd = 35;

//或

(*pPid).Kp = 15;
(*pPid).Ki = 25;
(*pPid).Kd = 35;

使用结构体指针变量的好处:通过指针传递可以减少内存的开销

关于结构体指针,大多数使用场景是:


void updatePid(PidController *pid)  // 传递指针,不拷贝数据
{  
    pid->Kp = 20;  // 直接修改原来的结构体

}

int main()
{
    PidController pid = {10, 10, 10};    //提前定义好普通结构体变量

    PidController *pPid = &pid
    updatePid(pPid );
}

当然还有一个更加简洁的方法那就是

void updatePid(PidController *pid)  // 传递指针,不拷贝数据
{  
    pid->Kp = 20;  // 直接修改原来的结构体

}

int main()
{
    PidController pid = {10, 10, 10};    //提前定义好普通结构体变量

    updatePid(&Pid );
}

2.2.3结构体数组

 对于结构体数组来讲,其定义为:

PidController pidArr[3];

如需调用结构体内的成员:

pidArr[0].Ki = 1;
pidArr[0].err = 2;

pidArr[1].Ki = 5;
pidArr[1].err = 2;

pidArr[2].Kp = 10;

对于结构体数组来讲,其优点为:所有结构体对象在内存中是连续存储的。

2.2.4结构体指针数组

/暂缺,等我后续补充

三、全局变量

全局变量:在函数外定义的变量,其生命周期是整个程序执行时间,在程序启动时就会分配内存。

过多的使用全局变量是一个并不太好的编程习惯,过多的全局变量会使得代码的可维护性差,并且耦合程度高,使得代码复用变得困难。

3.1全局变量的应用

关于全局变量的使用很简单,即在.c文件中的函数外部定义即可。

其正确使用如下:

//led.c文件中

uint16_t led_state = 0;

void led_init()
{

}


//main.c文件中
extern uint16_t led_state;

int main()
{

}

3.2全局变量使用的常见错误 

关于全局变量的使用,还存在这一些错误操作。如:

//在led.h文件中

uint16 led_state = 0;

//在main.c文件中
extern uint16 led_state = 0;

错误操作1:

当在.h文件中定义变量时,假如该.h文件被#include引用,则会造成全局变量在多个编译单元中重复定义,链接器会因为符号重复定义而报错。

头文件中是用来声明的,并不能用来定义。

错误操作2:

extern关键词是告诉编译器该变量在其他地方,并不能用来定义,其也是只能用来声明。

(这些是我之前经常出错的点)

3.3关键词static

关键词static:将全局变量的作用域限制在当前文件内,即使其他文件 #include 了该头文件,也无法访问此全局变量。它不会暴露给其他源文件。

通过该关键词,可以将全局变量限制在当前文件中,避免在其他文件中被意外访问或修改。

四、局部变量

4.1局部变量的定义

局部变量可以直接理解为在函数内部定义的变量

void calculate() {
    int result = 0;  // 这是一个普通的局部变量

    for (int i = 0; i < 10; i++) {
        result += i;
    }
}

上述代码中的变量result为局部变量,其每当程序进入函数calculate则会为变量result分配内存(sram),执行完函数之后改内存就会释放掉。可以一定程度的节约单片机资源。

4.2关键词static

提到局部变量就不得不说到关键词static,使用static关键词修饰之后,局部变量count的生命周期变成了整个程序运行期间。简单来说就是程序执行完之后,这个变量依旧还存在,并不会被清除。

void calculate() { 
    static int count;  // 静态局部变量
    count ++;
}

对于这个关键词,最常见的应用就是计进入某个函数的次数。

如上述程序的变量count,其数值就是为程序进入函数calculate的值。

五、常量

在c语言当中,常量的定义为:“在程序执行过程中不能被修改的量

常量的定义有两种方法:

  1. 通过关键词const来定义
  2. 通过宏定义#define

5.1使用const关键词定义的优缺点

  • 优点
  1. 通过const定义的变量是具备有符号、无符号等数据类型
  2. 通过const定义的变量是可以在调试器中查看和修改的

const定义的变量依旧存储在内存(sram)当中,。

关键词const的作用是让 变量 变成只读属性,其任是一个变量。

5.2使用#define宏定义的优缺点

  • 优点
  1. 宏定义不占用存储空间,即不存储在sram当中,也不存储在flash当中。仅仅是编译前的文本替换
  • 缺点
  1. 通过宏定义的常量,其无法再调试器中查看和修改,不方便我们调试。
  2. 并没有有符号,无符号这些类型,其只是简单的文本替换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值