C语言学习总结
C语言学习
-
-
一.指针
-
- 1.指针的概念
- 2.指针的定义
- 3.指针如何使用
-
- 3.1 指针声明
-
3.2 指针的基本操作
- 4.使用指针的意义
-
- (1)动态内存分配
-
(2)传递大对象
- 5.使用指针的注意事项
- 6.指针的高级使用方法
-
- 6.1 函数指针
-
6.2 指向指针的指针
- 7.指针和数组
- 8.指向指针的指针
-
- 8.1 定义
-
8.2 应用
-
二.结构体
-
-
- 2.1 结构体定义
-
-
2.2 结构体的访问
-
- 2.2.1 结构体的访问
- 2.2.2 使用箭头运算符 (->)
-
2.3 结构体指针
-
2.3.1 结构体指针的应用
-
-
- 结构体数组
-
3.1 结构体数组的本质
-
3.2 如何声明与定义结构体数组
-
3.4 对结构体数组进行初始化配置
-
3.5 获取、访问或操作结构体数组中的变量
-
4.结构体与函数
-
三.函数
-
- 1 定义
- 2.函数指针
-
- 2.1 函数指针的定义
-
四.动态内存管理
-
-
4.1 malloc 函数
-
4.2 free 函数
-
- 1函数原型
-
4.3 realloc函数
-
-
第五章 变量管理
* 5.2 几个特殊的变量声明
* * 5.2.1 Extern
* 5.2.2 static
* 5.2.3 volatile
* 六、if_else语句
* * 6.1 if 语句图
* 6.2 if -else 语句图
* 七、switch语句
* * 7.2语句表达式
* 7.3 程序图
* 八 运算符
* * 8.1 位运算
* * 8.1.1 ~操作
* 8.1.2 &操作
* 8.1.3 ^操作
* 8.1.4 | 操作
* 8.1.5 <<操作
* 8.1.6 >>操作
* 8.1.7 位运算与直接赋值的区别
* * 1.位操作:
* 2.状态保留
-
3. 清晰性
-
8. 1. 8 |= 和 & = 的区别
-
8. 2 增量运算符
-
- 1. 增量运算符基础介绍
-
- 2. 具体对比分析
-
- 2. 1 前缀增量运算 ++i
-
-
2. 2 后缀增量运算 i++
-
3.总结
-
4.示例
-
输出结果
- 8.3 逻辑运算符
- 8.4 运算优先级
-
九 typedf
-
- 9.1 作用
- 9.2 typedef和#define的区别
-
-
-
十进制ASCII编码...
-
十一标准库函数...
-
详细阐述scanf()函数的用法...
-
指针的差异...
- 11.2 strcpy()函数
-
- 11.2.1 作用
-
11.2.2 函数原型
-
11.2.3 参数说明
-
11.2.4 返回值
-
11.2.5 注意事项
-
11.2.6 示例
- 11.3 strcmp()函数
-
- 11.3.1 作用
-
11.3.2 函数原型
-
11.3.3 参数
-
11.3.4 返回值
-
11.3.5 使用示例
-
11.3.6 注意事项
- 11.4 getchar()函数
-
-
第十二章 数据类型与范围
-
- 第12.1节 基础数据类型
-
- 第12.2节 bit位+整型变量
-
- 第12.3节 数据衍生类
-
-
第12.4节 地址变量
-
十三、关键词
-
- 13.1 asm
- 13.2 code
-
- 1.code 关键字的含义
-
- 1.1 存储类型:
- 1.2 内存优化:
- 1.3 示例:
-
2 注意事项
-
-
2.1 可移植性:
-
2.2 替代方案:
-
13.3 xdata
-
- 13.3.1 存储类别
-
-
13.3.2 用途
-
13.3.3 性能考虑:
-
-
十四、枚举
* 14.1 枚举概念
* 14.2 实例说明
* 14.3 枚举的特点
* 14.3.1 自动生成赋值
* 14.3.2 类型安全
* 14.3.3 可读性
* 14.3.4 在编程中常用于处理位掩码问题:
- 注意事项
-
- 1 常见问题点
-
- 1.1 在头文件.h中进行变量声明
- 1.1.1 变量的说明与设置
- 1.1.2 变量的说明与设置
一.指针
1.指针的概念
指针实际上就是一个变量的地址,指针也是一个变量,只不过它存放的是一个变量的地址。指针指向某个变量的位置。
2.指针的定义
如何去定义指针,定义指针的方法有几种?
3.指针如何使用
3.1 指针声明
int *p;//这里定义了一个整型的指针变量p,其中*为指针符号;
3.2 指针的基本操作
(1)取变量地址
int x = 10;//定义一个整型变量,并将10赋值给x;
int *p = &x;//使用取地址符号&获取x的地址,并将这个地址赋值给指针变量p
(2)解引用
int x= 10;
int *p = &x;
printf("%d",*p);//取值运算符*获取指针p指向的变量的值;
4.使用指针的意义
(1)动态内存分配
(2)传递大对象
5.使用指针的注意事项
(1)空指针
(2)悬空指针
(3)指针运算
6.指针的高级使用方法
6.1 函数指针
6.2 指向指针的指针
7.指针和数组
8.指向指针的指针
8.1 定义
指向指针的指针实际上是一个指针变量的地址
int value = 0;
int *ptr = &value;
int **pptr = ptr;
printf("%d",value);
printf("%d",*ptr);
printf("%d",**pptr);
8.2 应用
(1)动态内存管理
(2)修改函数参数
(3)实现链表和其他数据结构
二.结构体
2.1 结构体定义
共有三种方法用于定义数据结构。这些方法包括:
一种方法是通过关键字struct来声明数据类型。
第二种方法采用union类型来实现数据存储。
第三种方法则利用指针变量指向特定的数据类型。
struct Student {
char name[50];
int age;
float gpa;
}; // 结构体类型定义结束
struct Student student1; // 在后面定义一个变量 student1
2.使用 struct 关键字直接定义结构体和结构体变量
struct Student {
char name[50];
int age;
float gpa;
} student2; // 这里定义了一个名为 student1 的结构体变量
3.使用 typedef 定义结构体类型的别名
typedef struct {
char name[50];
int age;
float gpa;
} Student; // 定义了一个 Student 类型
Student student3; // 现在可以直接使用 Student 来定义变量
2.2 结构体的访问
2.2.1 结构体的访问
#include <stdio.h>
struct Student {
char name[50];
int age;
float gpa;
};
int main() {
struct Student student1;
// 使用点运算符访问结构体成员
student1.age = 20;
snprintf(student1.name, sizeof(student1.name), "Alice");
student1.gpa = 3.5;
printf("Name: %s, Age: %d, GPA: %.2f\n", student1.name, student1.age, student1.gpa);
return 0;
}
2.2.2 使用箭头运算符 (->)
#include <stdio.h>
#include <stdlib.h>
struct Student {
char name[50];
int age;
float gpa;
};
int main() {
struct Student *studentPtr = malloc(sizeof(struct Student));
// 使用箭头运算符访问结构体成员
studentPtr->age = 21;
snprintf(studentPtr->name, sizeof(studentPtr->name), "Bob");
studentPtr->gpa = 3.8;
printf("Name: %s, Age: %d, GPA: %.2f\n", studentPtr->name, studentPtr->age, studentPtr->gpa);
free(studentPtr); // 释放动态分配的内存
return 0;
}
2.3 结构体指针
2.3.1 结构体指针的应用
1.直接访问结构体
#include <stdio.h>
struct Student {
char name[50];
int age;
float gpa;
};
int main() {
struct Student student1; // 定义结构体变量
struct Student *studentPtr; // 定义结构体指针
studentPtr = &student1; // 将指针指向结构体变量的地址
// 使用指针访问结构体成员
studentPtr->age = 22;
snprintf(studentPtr->name, sizeof(studentPtr->name), "Charlie");
studentPtr->gpa = 3.9;
// 输出结构体成员
printf("Name: %s, Age: %d, GPA: %.2f\n", studentPtr->name, studentPtr->age, studentPtr->gpa);
return 0;
}
2.动态分配结构体
#include <stdio.h>
#include <stdlib.h>
struct Student {
char name[50];
int age;
float gpa;
};
int main() {
struct Student *studentPtr = malloc(sizeof(struct Student)); // 动态分配内存
if (studentPtr == NULL) {
printf("Memory allocation failed.\n");
return 1; // 内存分配失败时退出
}
// 使用动态分配的指针访问结构体成员
studentPtr->age = 23;
snprintf(studentPtr->name, sizeof(studentPtr->name), "David");
studentPtr->gpa = 4.0;
// 输出结构体成员
printf("Name: %s, Age: %d, GPA: %.2f\n", studentPtr->name, studentPtr->age, studentPtr->gpa);
free(studentPtr); // 释放动态分配的内存
return 0;
}
第三步:使用结构体指针作为函数参数 是一种可行的方式
#include <stdio.h>
struct Student {
char name[50];
int age;
float gpa;
};
// 函数接受结构体指针作为参数
void updateStudent(struct Student *s, const char *name, int age, float gpa) {
snprintf(s->name, sizeof(s->name), "%s", name);
s->age = age;
s->gpa = gpa;
}
int main() {
struct Student student;
// 更新结构体内容
updateStudent(&student, "Eve", 24, 3.7);
// 输出更新后的结构体内容
printf("Name: %s, Age: %d, GPA: %.2f\n", student.name, student.age, student.gpa);
return 0;
}
3.结构体数组
3.1 结构体数组的概念
结构体数组是可以存储多个结构体变量的数组(本质上是数组,只不过存储的元素是结构体变量)
3.2 结构体数组的声明和定义
//方式一
struct Book
{
结构体成员1;
结构体成员2;
};
struct Book book[10];
//方式2
struct Book
{
结构体成员1;
结构体成员2;
}book[10];
3.4 结构体数组的初始化
3.5 结构体数组变量的访问
4.结构体与函数
三.函数
1 定义
void printf(int a,int b)//void 返回值类型,int 形参
{
函数体;//要执行的语句块
}
2.函数指针
2.1 函数指针的定义
四.动态内存管理
4.1 malloc 函数
malloc是C的标准库函数
2.它可以动态的在存储空间中申请一块空间;
3.语法`在这里插入代码片`
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *ptr;
ptr = (int*)malloc(sizeof(int));
if(ptr==NULL)
{
printf("内存申请失败!");
exit(1);
}
printf("请输入一个整数:");
scanf("%d",ptr);
printf("%d",*ptr);
return 0;
}
4.2 free 函数
1函数原型
void free(void *ptr)
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *ptr;
ptr = (int*)malloc(sizeof(int));
if(ptr==NULL)
{
printf("内存申请失败!");
exit(1);
}
printf("请输入一个整数:");
scanf("%d",ptr);
printf("%d",*ptr);
free(ptr);
printf("%d",*ptr);//内存空间已经被释放,无法打印
return 0;
### 4.3
4.3 realloc函数
五、变量
全局性与局部性的关系是什么?
在编写程序时遇到了一个问题:
在主程序(主函数)内部声明了一个标识符(相当于一个名字)。当调用另一个辅助程序(子函数)的过程中发现该标识符已经被重新定义了位置或值,则这会导致该辅助程序无法正常运行或编译出现错误。
5.1 局部变量和全局变量
5.1.1 局部变量
- 声明局部变量
- 生命周期:局部变量由函数或代码块启动时创建,在该函数/代码块退出时自然消失
- 访问权限:仅允许在当前函数或代码块范围内进行访问操作
5.1.2 全局变量
- 定义;在整个程序中声明了使用的变量;
- 生命周期: 该过程的持续时间为从程序启动到完成;
- 访问: 该系统能够获取整个程序所有函数的数据
5.1.3 全局变量和局部变量的关系
- 当出现同名时,地方变量会取代地方变量;
- 地方变量位于栈中,而全局变量则位于静态存储区。
5.2 几个特殊的变量声明
5.2.1 Extern
5.2.2 static
这个关键字表示 adtimes 的存储持续时间是整个程序的运行周期,而不是局部变量的生命周期。它只在声明它的文件内可见(文件作用域),而不是其他文件或模块。由于它是静态的,变量在程序开始时初始化为 0,并且在函数调用之间保持其值。
5.2.3 volatile
该关键字向编译器传达信息,
表明该变量可能在程序运行的不同部分被异步修改,
特别是在中断服务例程等非主程序区域。
优化代码时应避免对其做任何假设或利用缓存,
必须每次访问时重新读取当前值。
这一机制多用于硬件密集型的应用场景中,
以确保数据一致性。
static volatile unsigned char adtimes;
声明了一个名为adtimes的静态变量;该变量可在中断或其他非主程序事件中进行修改;其数值维持不变直至程序完成运行;且数值范围限定于0至255之间。此类变量常用于计数或状态标记;特别适用于实时系统和嵌入式开发场景。
六、if_else语句
6.1 if 语句图

6.2 if -else 语句图

七、switch语句
7.2语句表达式
switch(表达式)
{
case 常量表达式1:语句1;break;
case 常量表达式2:语句2;break;
...
case 常量表达式n:语句n;break;
default:语句;break;
}
7.3 程序图

八 运算符
8.1 位运算
8.1.1 ~操作
按位取反
~1111 0101 = 0000 1010
8.1.2 &操作
当两个操作数中有一个是零时,则按位与运算的结果即为零;
只有当这两个操作数均为一的时候(即每一位都取"与"),按位与运算的结果才会是零。
运算结果出现零的情况即表示两位操作数中至少有一位是零。
(a \cdot b) = c
一旦发生错误,则系统将输出当前高位有效位减去一后的值。
8.1.3 ^操作
对应的数不同结果为1,相同为0;
8.1.4 | 操作
两个位,有一个位为1,结果就是1;
8.1.5 <<操作
1<<3;表示将1向左移动3位;
例子:1<<3 0000 0001左移3位 0000 1000 为 0x80;
8.1.6 >>操作
8.1.7 位运算与直接赋值的区别
例如:|= 采用|=而非直接赋值的主要原因在于旨在保留原有值的部分位并同时加入新值的位。
1.位操作:
|= 用于执行按位“或”操作,这意味着你可以将某些位设为1,而不影响其他位。例如,如果你想将某些标志位开启,而不改变其他标志位的状态,使用 |= 是很合适的。
2.状态保留
通过 |=,你可以保留变量的现有状态。例如,如果变量 a 当前值是 0101(5),而你希望开启某些标志(比如与 0011(3)按位“或”),结果将是 0111(7),只修改需要的位。
3.简洁性
使用 |= 可以使代码更简洁、更易读,减少了对变量值的重复引用。
8.1.8 |= 与& = 的区别
|= 用于在保持其他位不变的前提下,实现对目标位置1的赋值;
&= 用于在保持其他位不变的前提下,实现对目标位置0的赋值;
8.2 ++自增操作符
1.自增操作符概述
- ++i(即
Increment Prefix)是一种前缀自增操作符,在执行该操作时会首先将变量i的值加1后再赋值给同一个变量。- i++(即
Increment Postfix)是一种后缀自增操作符,在执行该操作时会首先输出变量i的当前数值(不包括本次增加),然后再将变量i的值增加1。
- i++(即
2. 详细对比
2.1 前缀自增 ++i
- 语法: ++i
- 执行顺序
i 的值增加 1。
返回增加后的值。
int i = 5;
int j = ++i; // i 变为 6,j 也被赋值为 6
- 结果:
i = 6, j = 6
2.2 后缀自增 i++
- 语法: i++
- 执行顺序
返回 i 的当前值。
i 的值增加 1。
int i = 5;
int j = i++; // j 被赋值为 5,i 变为 6
- 结果:
i = 6, j = 5
3.总结
- ++i 是前缀自增,先增加再返回。
- i++ 是后缀自增,先返回再增加。
4.示例
#include <stdio.h>
int main() {
int i = 5;
// 使用前缀自增
int a = ++i; // i 变为 6, a = 6
printf("前缀自增: a = %d, i = %d\n", a, i);
// 使用后缀自增
int b = i++; // b = 6, i 变为 7
printf("后缀自增: b = %d, i = %d\n", b, i);
return 0;
}
输出结果
前缀自增: a = 6, i = 6
后缀自增: b = 6, i = 7
8.3 逻辑运算符
1.|| 或:至少有一个条件为真即为真
8.4 运算优先级
| 优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
|---|---|---|---|---|---|
| 1 | |||||
| 2 | ! | 逻辑非运算符 | !表达式 | 从右到左 | 单目运算符 |
| 7 | == | 等于运算符 | 表达式 == 表达式 | 从左到右 | 双目运算符 |
| 12 | 或 | 逻辑或 | 表达式 或 表达式 | 从左到右 | 双目运算符 |
九 typedf
9.1 作用
用于给已有的数据类型定义一个新的名字
9.2 typedef和#define的区别
主要适用于当你需要定义新类型的场合,在确保类型安全性和易读性的前提下,请选用typedef这一关键字。对于那些仅需实现简单常量或宏替换的需求,则应采用#define这一预处理器指令。然而,在使用#define时,请务必注意潜在风险与较高的调试复杂度。
十 ACSII码
| 二进制 | 十进制 | 十六进制 | 字符/缩写 | 解释 |
|---|---|---|---|---|
| 0010 0000 | 32 | 20 | (space) | 空格 |
| == | 等于运算符 | 表达式 == 表达式 | 从左到右 | |
| 12 | 逻辑或 | 表达式 或 表达式 | 从左到右 |
十一 标准库函数
11.1 scanf()函数
1.在输入整型数据和字符型数据,指针的区别
对于整型数据,使用 & 取地址。
对于单个字符,使用 & 取地址。
对于字符串,直接使用数组名(自动转换为指针)。
11.2 strcpy()函数
11.2.1 作用
实现字符串的复制过程。该函数的定义位于 string.h 头文件中。
11.2.2 函数原型
char *strcpy(char *dest, const char *src);
11.2.3 参数说明
dest:目标字符串,即要将内容复制到的字符串(必须有足够的空间容纳源字符串)。
src:源字符串,即要被复制的字符串。
11.2.4 返回值
strcpy() 函数返回 dest 的指针,即目标字符串的地址。
11.2.5 注意事项
为了使 dest 能够容纳 src 字符串及其尾随空字符(‘\0’)而没有溢出,在使用 strcpy() 时,请确保目标缓冲区具备足够的容量。
首先需注意的是, 由于 copy 操作不会对目标缓存进行预先检查, 如果目标缓存容量不足, 将会引发溢出问题并导致不可预测的行为。
对此, 建议采用更安全且可靠的 strncpy() 函数或其它安全替代方案以避免此类风险。
11.2.6 示例
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "Hello, World!";
char destination[50]; // 确保目标数组足够大
strcpy(destination, source); // 复制字符串
printf("Source: %s\n", source);
printf("Destination: %s\n", destination);
return 0;
}
11.3 strcmp()函数
11.3.1 作用
strcmp() 是 C 语言中的一个字符串比较函数,用于比较两个字符串的内容。它在 <string.h> 头文件中声明,并且其功能是逐字节比较两个字符串,直到遇到不同字符或遇到字符串结束标志(\0)。
11.3.2 函数原型
int strcmp(const char *str1, const char *str2);
11.3.3 参数
str1: 第一个要比较的字符串。
str2: 第二个要比较的字符串。
11.3.4 返回值
小于 0: 如果 str1 在字典顺序上小于 str2。
等于 0: 如果 str1 和 str2 相等。
大于 0: 如果 str1 在字典顺序上大于 str2。
11.3.5 使用示例
#include <stdio.h>
#include <string.h>
int main() {
const char *str1 = "apple";
const char *str2 = "banana";
int result = strcmp(str1, str2);
if (result < 0) {
printf("\"%s\" is less than \"%s\"\n", str1, str2);
} else if (result > 0) {
printf("\"%s\" is greater than \"%s\"\n", str1, str2);
} else {
printf("\"%s\" is equal to \"%s\"\n", str1, str2);
}
return 0;
}
11.3.6 注意事项
strcmp() 是区分大小写的。例如,"hello" 和 "Hello" 会被认为是不同的字符串。
比较时,如果字符串长度不相同,但前面部分相同,strcmp() 只会返回小于 0 或大于 0 的结果,取决于第一个不同字符的 ASCII 值。
在使用 strcmp() 时确保输入的字符串都是以 \0 结尾的有效字符串。
11.4 getchar()函数
十二 数据类型及范围
12.1 基本数据类型
| 数据类型 | 作用 | 数据范围 |
|---|---|---|
| 整型(int) | 存储整数 | 通常为2个字节,某些系统为2字节或8字节 |
| char(字符型) | 存储单个字符 | 1个字节 |
| float(单精度浮点型) | 用于存储单精度浮点数 | 通常为4个字节 |
| double(双精度浮点型) | 用于存储双精度浮点数 | 通常为8个字节 |
12.2 修饰符+整数 数据类型
| 数据类型 | 作用 | 数据范围 |
|---|---|---|
| signed int(有符号整数:默认) | 存储整数 | 4字节: (-2,147,483,648) 到 (2,147,483,647) |
| unsigned int(无符号整数) | 4字节: (0) 到 (4,294,967,295) | |
| short int (短整型) | 2个字节 | |
| long(长整型) | 4-8字节 |
12.3 派生数据类型
| 数据类型 | 作用 | 数据范围 |
|---|---|---|
| 数组 | ||
| 结构体 | ||
| 共用体 | 2个字节 | |
| 枚举 | 4-8字节 |
12.4 指针数据类型
十三、关键词
13.1 asm
在 C 语言中使用 asm 关键字来插入汇编代码
13.2 code
1.code 关键字的含义
1.1 存储类型:
code 主要用于位于只读存储器(ROM)或 Flash 存储器中的变量定义。这表明这些变量在程序运行期间无法被修改,并且适合用来存储静态常量数据或必要的代码信息。
1.2 内存优化:
在嵌入式系统中,由于 RAM 容量受限,在存储一些固定数据时将其放入 Flash 可以节省 RAM 使用并提升内存利用效率。
1.3 示例:
unsigned char code PLACE_CODE[] = {0xFE, 0xFD, 0xFB, 0xF7};
2 注意事项
2.1 可移植性:
由于代码作为某些编译器的扩展存在,并非ANSI C标准的部分内容,在不同编程环境中应用时需格外小心以确保兼容性
2.2 替代方案:
在那些不具备代码关键字支持的编译器中,我们能够通过常量关键字以及适当的数据类型来实现类似的效果。
const unsigned char PLACE_CODE[] = {0xFE, 0xFD, 0xFB, 0xF7};
13.3 xdata
xdata 常用于某些嵌入式编程场景中的存储修饰符,在基于 8051 微控制器的项目中(尤其是在使用 Keil C 编译器时)特别适用。以下将对 xdata 进行详细阐述:
13.3.1 存储类别
xdata 代表"外部数据存储器"(Externally Mounted Memory),它被用来指定在外设微控制器的外设RAM中定义的变量位置。这与其内部的数据存储器idata相对应。在基于8051系列的嵌入式系统中,默认配置下的内存资源较为有限,在这种情况下通常需要依赖于外设RAM来实现对更多数据信息的有效加载与处理。
13.3.2 用途
通过 xdata 可以创建大型数组或占用较多内存的变量;由于外部存储如 RAM 一般具有较大的容量。该系统可用于管理那些在程序运行时需要动态分配内存并可进行修改的操作。
13.3.3 性能考虑:
xdata中的数据读取速度较慢,通常低于idata.这可能是因为外部存储设备的速度较慢.这也可能导致程序运行时因频繁调用xdata而出现性能瓶颈.
在实际应用中使用xdata时需权衡内存占用与读取速度的关系,以保证系统的高效运行.
十四、枚举
14.1 枚举定义
在C语言中描述的枚举(enums)是一种自定义的数据类型,在编程中被用来创建一组命名的整数值常量。这种数据类型通常被用来提高代码的一致性和维护性。
enum enum_name {
constant1,
constant2,
constant3,
...
};
14.2 示例
#include <stdio.h>
// 定义一个枚举类型,表示颜色
enum Color {
RED, // 默认值为 0
GREEN, // 默认值为 1
BLUE // 默认值为 2
};
int main() {
// 声明一个枚举变量
enum Color myColor;
// 将枚举变量赋值
myColor = GREEN;
// 根据枚举值打印颜色
switch (myColor) {
case RED:
printf("The color is Red\n");
break;
case GREEN:
printf("The color is Green\n");
break;
case BLUE:
printf("The color is Blue\n");
break;
default:
printf("Unknown color\n");
}
return 0;
}
The color is Green
14.3 枚举的特性
14.3.1 自动赋值
枚举常量从 0 开始自动赋值,依次递增。可以手动指定值。例如:
enum Color {
RED = 1, // RED 指定为 1
GREEN = 2, // GREEN 指定为 2
BLUE = 4 // BLUE 指定为 4
};
14.3.2 类型安全
枚举能够提供更强的类型安全,在相较于普通整数常量时更加稳妥地避免将错误的值赋给变量。
14.3.3 可读性
使用枚举可以使代码更易于理解。例如,myColor = GREEN; 比 myColor = 1; 更直观。
14.3.4 可以用于位掩码:
enum Flags {
FLAG_A = 1 << 0, // 0001
FLAG_B = 1 << 1, // 0010
FLAG_C = 1 << 2 // 0100
};
int myFlags = FLAG_A | FLAG_C; // 设置 FLAG_A 和 FLAG_C
适用场景
状态标识:用于表示不同的系统状态信息,例如网络运行状态、设备工作状态等信息的展示与管理机制设计基础。
命令标识:可以用来区分不同种类的执行指令或操作指令形式,在程序流程控制中起到明确区分的作用。
错误标识:通过定义不同类型的错误代码来提高程序的可读性和维护效率。
注意事项
1 常见错误
1.1 在.h文件中定义变量
1.1.1 变量的定义与声明
- 定义:为变量分配内存
int myVar = 0; // 这是一个定义
- 声明:告知编译器变量的存在,但不分配内存
extern int myVar; // 这是一个声明
1.1.2 变量的定义与声明
- 当你在 .h 文件中定义变量时,比如:
int myVar = 0; // 在头文件中定义
可能会引起所有包含该头文件的.c文件均试图为变量myVar做出声明。
在编译时执行链接阶段时会出现多个关于变量myVar的声明。
这将导致构建过程中出现与变量myVar相关的重复声明问题
最佳做法是建议在包含所有源文件的头文件中进行变量声明,并在独立于所有其他源文件的.c文件中进行定义。如以下情况所示:
在 myheader.h 中
extern int myVar; // 声明
- 在 myheader.c 中
int myVar = 0; // 在 C 文件中定义
为了避免出现重复定义的问题,在头文件中通常会使用 extern 关键字声明变量,并且同时在单个源代码文件中进行具体定义。这种做法能确保全局变量在整个程序系统内只有一个实例,并且能够使得多个独立的源代码文件能够共享同一个全局变量。
