当前位置: 首页 > news >正文

c语法高阶—(联合体,枚举,位域,编译器,宏定义,条件编译,条件编译,头文件)

目录

一 联合体(重要)

特性

总结

二 枚举(重要)

特性

总结:

三 位域(了解)

定义

特性

使用场景

优缺点分析表

位域的特点和使用方法

总结:

四 编译器(linux)(重要)

常用编译选项

分阶段编译流程

1. 预处理(此时假设要处理的文件为hello.c)

2. 编译

3. 汇编

4. 链接

 基本使用流程

1. 编写源代码

2. 完整编译(一步到位)

五  宏定义加强(重要)

六 条件编译(重要)

七 头文件(重要)

项目结构 

​编辑

1. 创建头文件(utils.h)

2. 实现文件(utils.c)

3. 主程序(main.c)

编译与使用 

八,typedef关键字


一 联合体(重要)

特性

特性联合体(union)结构体(struct)
定义方式union 名称 { 类型 成员; ... };struct 名称 { 类型 成员; ... };
内存分配所有成员共享同一内存地址每个成员有独立内存地址
存储空间大小为最大成员的尺寸大小为所有成员尺寸之和(含填充)
访问方式同一时刻只能有效访问一个成员可同时访问所有成员
类型安全低(需自行管理当前有效类型)高(成员类型明确)
典型应用类型转换、协议解析、硬件寄存器访问数据聚合、对象建模
赋值

当给联合一个成员赋值名,其他成员的值则失效

不会失效

总结

C 共用体 | 菜鸟教程

之上链接足可以了解,同一时间,联合体只能有效访问一个成员。

#include <stdio.h>/*
联合体与结构体非常相似,但是他们本质上完全不一样
结构体每个成员都有独立的内存
联合体的成员共用一个内存空间,因此联合体也被称为共用体语法:
union 标签{}联合体可以定义多个成员,同一时间,只有一个内存,只有一个成员的值有效1.尺寸由成员最大决定2.成员地址一样,一个内存空间3.为一个成员赋值,其他成员值失效
*/union wahaha
{int a;char c;long age;char w;
};
int main(int argc, char const *argv[])
{union wahaha www;printf("%lu\n", sizeof(www.age));return 0;
}

二 枚举(重要)

特性

特性/场景说明
定义方式enum 名称 { 常量1, 常量2... };
常量管理集中管理相关常量,避免魔法数字
可读性使用有意义的名称代替原始值
类型安全C中本质是int,可隐式转换;C++中为独立类型
内存占用通常使用int存储(4字节),编译器可能优化
适用场景状态码、有限选项集合、模式标志、错误代码
优势代码自文档化、易维护、避免重复值冲突
缺点作用域污染(C中枚举常量全局可见)、无法表示复杂数据类型

总结:

#include <stdio.h>//方案1 用宏定义
// #define MON 1
// #define TUE 2
// #define WED 3
// #define THU 4
// #define FRI 5
// #define SAT 6
// #define SUN 7/*枚举C语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。
枚举类型通常用于为程序中的一组相关的常量取名字,以便以程序的可读性和维护性;语法:enum 标签{常量1,常量2...};c语言中的枚举中的元素数据类型都必须是整型 int(有符号、无符号均可)
*//*
1.枚举中的常量第一个不赋值 默认为0
2.后续不赋值默认在前一个基础+1*/
enum DAY{MON=1,TUE=2,WED=3,THU=4,FRI=5,SAT=6,SUN
};int main(int argc, char const *argv[])
{enum DAY d1=SUN;printf("%d\n",d1);d1=MON;printf("%d\n",d1);d1=TUE;printf("%d\n",d1);//枚举的元素遍历(里面的值必须都是连续的),否则不能执行下面的遍历for (d1  = MON; d1<= SUN; d1++){printf("%d\n",d1);}return 0;
}

三 位域(了解)

定义

特性描述限制条件
定义方式在结构体中使用类型 成员名 : 位数;声明位数需≤类型长度(int通常≤32)
内存分配按需分配位空间,可能跨字节存储编译器决定具体布局
取值范围有符号类型保留1位符号位无符号类型可多用1位
访问方式使用成员运算符.访问无法取地址(&操作非法)
填充规则相邻位域类型相同时可能合并存储不同类型通常换存储单元
典型应用硬件寄存器、协议字段、标志位集合内存敏感场景

特性

维度位域常规变量
存储粒度位级(1-32位)字节级(8的倍数)
内存效率高(紧凑存储)低(可能浪费空间)
访问速度较慢(需位操作)较快(直接访问)
可移植性低(编译器实现差异)高(标准明确)
调试可见性差(二进制形式)好(直接查看值)
适用场景硬件寄存器、协议字段常规数据处理

使用场景

应用场景示例说明优势体现
硬件寄存器外设控制寄存器位映射精确控制硬件位
网络协议TCP头中的标志位(SYN/ACK等)紧凑解析协议字段
嵌入式系统设备状态标志集合节省有限内存资源
压缩存储存储大量布尔值减少内存占用(8倍于bool数组)
数据包格式自定义二进制协议精确控制每个位的含义

优缺点分析表

优点缺点
1. 极致内存利用率1. 可移植性差
2. 直观操作硬件寄存器2. 访问速度较慢
3. 提升标志位代码可读性3. 调试困难
4. 减少位操作代码复杂度4. 不可取地址
5. 自动位掩码生成5. 类型限制(通常整型

位域的特点和使用方法

  • 定义位域时,可以指定成员的位域宽度,即成员所占用的位数。
  • 位域的宽度不能超过其数据类型的大小,因为位域必须适应所使用的整数类型。
  • 位域的数据类型可以是 intunsigned intsigned int 等整数类型,也可以是枚举类型。
  • 位域可以单独使用,也可以与其他成员一起组成结构体。
  • 位域的访问是通过点运算符(.)来实现的,与普通的结构体成员访问方式相同。

总结:

1.节约内存,其他还算简单。


问题一:

    my.b = 10;                     // 有符号的

    printf("b=%d\n", my.b);  //输出为-6

解答:

#include <stdio.h>/*
C语言的位域(bit-field)是一种特殊的结构体成员,允许我们按位对成员进行定义,指定其占用的位数;
指定占用位数的数据类型只能是int/unsigned int;位域与共用体,其共同目的是减少内存的使用,提高运行的效率,这在板子开发中,有极其重要的作用
*/struct bf
{// 指定的位数不能超过C语言规定的位数大小int a : 4;      // 指定其占用4位int b : 4;      // 指定其占用4位unsigned c : 2; // 指定其占用2位// 空域,占着位数,但是不能赋值,没给名字int : 4; // 指定其占用4位// 从下一个单元开始存储新的成员数据unsigned d : 4;
};int main(int argc, char const *argv[])
{// 声明该位域的 变量struct bf my;// 1.为a赋值,a的位数为18,即 00 0000 0000 0000 0000my.a = 1;printf("a=%d\n", my.a);// 2.为b赋值,b的位数为4,即0000my.b = 5; // 有符号的printf("b=%d\n", my.b);// 3.无符号,为c赋值,c的位数为0,即00my.c = 3; // 无符号的printf("c=%d\n", my.c);// 4.无符号,为c赋值,c的位数为4,即0000my.d = 13; // 无符号的printf("d=%d\n", my.d);// 4.无符号,为c赋值,c的位数为4,即0000my.b = 10; // 有符号的printf("b=%d\n", my.b);// 4.无符号,为c赋值,c的位数为4,即0000my.d = 10; // 无符号的printf("d=%d\n", my.d);// 求其字节数,按照最大的进补原则printf("%lu\n", sizeof(my)); // 4return 0;
}

四 编译器(linux)(重要)

常用编译选项

选项作用描述示例
-o <file>指定输出文件名gcc -o demo demo.c
-Wall开启所有警告信息gcc -Wall test.c
-g包含调试信息(GDB使用)gcc -g debug.c
-O<level>优化级别(0-3,s)gcc -O2 optimize.c
-I<dir>指定头文件搜索目录gcc -I./include src.c
-L<dir>指定库文件搜索目录gcc -L./lib main.c -lmylib
-l<library>链接指定库gcc main.c -lm(链接数学库)
-D<macro>定义预处理宏gcc -DDEBUG debug.c

分阶段编译流程

1. 预处理(此时假设要处理的文件为hello.c)
gcc -E hello.c -o hello.i

作用

  • 展开头文件(#include

  • 宏替换(#define

  • 删除注释

  • 添加行号标识

2. 编译
gcc -S hello.i -o hello.s

生成:汇编代码文件(hello.s

3. 汇编
gcc -c hello.s -o hello.o

生成:目标文件(hello.o,二进制机器码)

4. 链接
gcc hello.o -o hello

作用:链接库文件和其他目标文件生成可执行文件

 基本使用流程

1. 编写源代码
// hello.c
#include <stdio.h>
int main() {printf("Hello, Linux C!\n");return 0;
}
2. 完整编译(一步到位)
gcc hello.c -o hello  # 编译生成可执行文件hello
./hello               # 运行程序

输出

Hello, Linux C!

五  宏定义加强(重要)

宏(macro)其实就是一个特定的字符串,用来直接替换,编译的时候直接替换为对应的宏的内容(字符串替换);

宏的作用:

   1.使得程序的可读性有所提高,使用一个有意义的单词来表示一个无意义数字(某个值)

   2.方便对代码进行迭代更新,如果代码中有多处使用到该值,则只需要修改一处即可

   3.提高程序的执行效率,可以使用宏来实现一个比较简单的操作,用来替代函数的调用(宏表达式)

#include <stdio.h>
/*
宏,就是一个特定的字符串
*/// 1.无参,就是定义常量
#define a 1// 无值宏
#define NO_VALUE// 3.有参
// 宏只能写在一行(逻辑行),多行红使用转义符\来链接
// 本质是字符串替换,有时候会出现逻辑错误,需要使用()提升优先级。
#define MAX(a, b) a > b ? a : bint main(int argc, char const *argv[])
{// 1. 使用无参数宏,就是一个预处理的模板,其实就是定义常量printf("%d\n", a);// 2.无值宏int c = 1;int d = 2;int e = (c, d == 100 ? 100 : 200 ? 1000: 2000);printf("%d\n", e);return 0;
}

六 条件编译(重要)

指令描述
#define定义宏
#include包含一个源代码文件
#undef取消已定义的宏
#ifdef如果宏已经定义,则返回真
#ifndef如果宏没有定义,则返回真
#if如果给定条件为真,则编译下面代码
#else#if 的替代方案
#elif如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个 #if……#else 条件编译块
#error当遇到标准错误时,输出错误消息
#pragma使用标准化方法,向编译器发布特殊的命令到编译器中
#include <stdio.h>/*
条件编译:根据某一个条件来决定,是否需要编译,不同的操作系统,编译不同*/// 1.无值宏
#define HERON ;// 有值宏 条件编译
// 使用有值红田间只能是数字bool值,不可以是字符串
#define M1 0
#define M2 0
#define M3 0int main(int argc, char const *argv[])
{/*无值宏的使用*/
#ifdef HERONprintf("已经定义无值宏\n");
#endif#ifndef HERONprintf("无定义无值宏\n");
#endif/*有值宏条件编译1*/
#if M1printf("有值0000000\n");
#elif M2printf("有值aaaaaaaa\n");
#elif M3printf("有值9999999\n");
#elseprintf("都不成立\n");
#endifreturn 0;
}

七 头文件(重要)

特性描述
文件扩展名.h
主要作用声明函数原型、宏定义、类型定义等公共内容
包含方式#include <系统头文件> 或 #include "自定义头文件"
编译阶段预处理阶段展开(gcc -E可查看)
最佳实践1. 使用头文件守卫
2. 声明与实现分离
3. 避免包含实现代码

项目结构 

1. 创建头文件(utils.h)
#ifndef UTILS_H    // 头文件守卫开始
#define UTILS_H// 函数声明
int add(int a, int b);
void print_result(int result);// 宏定义
#define MAX(a, b) ((a) > (b) ? (a) : (b))// 结构体声明
struct Point {int x;int y;
};#endif // UTILS_H   // 头文件守卫结束
2. 实现文件(utils.c)
#include <stdio.h>
#include "utils.h"  // 包含自定义头文件int add(int a, int b) {return a + b;
}void print_result(int result) {printf("结果:%d\n", result);
}
3. 主程序(main.c)
#include <stdio.h>   // 系统头文件
#include "utils.h"   // 自定义头文件int main() {struct Point p = {10, 20};int sum = add(p.x, p.y);printf("最大值:%d\n", MAX(p.x, p.y));print_result(sum);return 0;
}

编译与使用 

看不到问我

gcc ./file/*.c -o ./指定文件目录/自定义文件名 -I ./头文件的文件目录名

八,typedef关键字

c语言提供了typedef关键字,为类型取一个新的名字

C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。下面的实例为单字节数字定义了一个术语 BYTE

#include <stdio.h>
/*
c语言提供了typedef关键字,为类型取一个新的名字
*/// 1.1
// 关键字
typedef int my_int;// 1.2 给枚举/结构体/共用体/位域 取别名
// 共用体 联合体取别名
typedef union gift
{int a;         // 4char c;        // 1long age;      // 8char name[32]; // 32
} gg;int main(int argc, char const *argv[])
{// 1.my_int i = 1;printf("%d\n", i);// 2.gg g1;int test = g1.a = 1;printf("%d\n", test);return 0;
}

 

相关文章:

  • zst-2001 历年真题 知识产权
  • Unable to ping server at localhost:1099解决
  • 第十二节:图像处理基础-图像平滑处理 (均值滤波、高斯滤波、中值滤波)
  • HTTP请求与缓存、页面渲染全流程
  • React学习路线图-Gemini版
  • Linux基本操作——网络操作文件下载
  • Selenium的driver.get_url 和 手动输入网址, 并点击的操作,有什么不同?
  • ZYNQ笔记(十八):VDMA VGA彩条显示
  • 打造个人知识库,wsl+ollama部署deepseek与vscode集成
  • 偏导数和梯度
  • IoTDB端边云同步技术的五大常见场景及简便使用方式
  • Filecoin矿工资金管理指南:使用lotus-shed actor withdraw工具
  • 【uniapp】errMsg: “navigateTo:fail timeout“
  • 如何评价大语言模型架构 TTT ?模型应不应该永远“固定”在推理阶段?模型是否应当在使用时继续学习?
  • Spring Boot 中如何解决 CORS 问题(详解)
  • 智慧城市的数据共享与协作:如何用大数据构建未来城市?
  • LVGL -meter的应用
  • 可编辑218页PPT | 基于数据运营的新型智慧城市实践与思考
  • 记录学习的第三十五天
  • C# 引用类型作为值参数与引用参数的区别
  • 普京提议于15日在土耳其恢复俄乌直接谈判
  • 肖峰读《从塞北到西域》︱拉铁摩尔的骆驼
  • 价格周报|供需回归僵局,本周生猪均价与上周基本持平
  • 智利观众也喜欢上海的《好东西》
  • 时隔14个月北京怀柔区重启供地,北京建工以3.59亿元摘得
  • 马上评丨规范隐藏式车门把手,重申安全高于酷炫