C语言八股---预处理,编译,汇编与链接篇
前言
从多个.c文件到达一个可执行文件的四步:
预处理–>编译–>汇编–>链接
预处理
预处理过程就是预处理器处理这些预处理指令(要不然编译器完全不认识),最终会生成 main.i的文件
主要做的事情有如下几点:
- 展开头文件
- 展开宏
- 条件编译
- 删除注释
- 添加行号等信息
- 保留parama预处理指令
- 头文件展开—#include指令
- #include <sdtio.h> 和 #include “stdio.h”
对于<> 搜索顺序为- 通过GCC参数gcc-I指定的目录(注:大写的I 让我们自由指定的)。
- 通过环境变量CINCLUDEPATH指定的目录。
- GCC的内定目录。
对于 " "的搜索时顺序 - 项目当前目录(此时也可以用"…/LED/led.h"方式去搜索)
- 通过GCC参数gcc-I指定的目录。
- 通过环境变量CINCLUDEPATH指定的目录。
- GCC的内定目录
- 为什么把声明放在头文件里
- 提供一个接口 方便其他文件通过声明调用对应的函数
- 当我们的led.c 包含了 led.h的时候 也方便编译器做类型的检查
- 头文件多次包含会增加可执行文件的体积吗?
只要是使用了类似#pragma once 或者#ifndef 多次包含是不会的增加可执行文件的体积的
同样的要注意:声明不会增加可执行文件的体积
- #include <sdtio.h> 和 #include “stdio.h”
- 宏展开#define 宏指令
- 宏定义最小
#define MIN(x,y) ((x) > (y) ? (y) : (x))
因为宏只是做了一个替换所以对于如下代码
- 宏定义最小
#include <stdio.h>#define MIN(x,y) ((x) > (y) ? (y) : (x))//因为宏只是做了一个替换所以对于如下代码int main(){int a = 2 ;int b = 5;int c = MIN(a++,b++);printf("c = %d a = %d b = %d\r\n",c,a,b); // a竟然等于4}
在这里可以用GNU C语法中的一些小技巧操作
#define MIN(x,y) ({\typeof(x) _x = (x);\typeof(y) _y = (y);\_x > _y ? _x : _y;})
- 定义一个很大的常数的时候
#define MAX_LONG (100001000010000)UL //指定类型 - ##连接符
高端用法 看了好多代码都用这个 但是分析起来乱乱的,大概就是把两个字母连接到一起
#define contact(x,y) (x##y)int bc = 50;printf("bc = %d\r\n", contact(b,c));
- offset_of与container_of
之前写结构体的时候写过,权当复习一下#define offset_of(type, member) ((size_t)(&((type *)0)->member)) #define container_of(type,member,ptr) (type *)((size_t) ptr - offset_of(type,member)) struct student {int height;char * name; }; int main() {struct student stu;stu.height = 50;stu.name = "123456";char ** tmp_name = &stu.name;struct student* s = &stu;printf("%p %p %ld\r\n",s, tmp_name,offset_of(struct student,height));struct student *new_s = container_of(struct student,name,tmp_name);new_s->height = 60;printf("%d\r\n",stu.height); }
``
- 宏为什么要用 do {} while(0)
如果去看linux源码也好还是RTOS等的代码也好 会有很多时候用到do_while(0) 它的作用是什么呢
假设我们定义了
#define MACRO() foo(); bar()
此时我们写了这样的伪代码
if (condition)
MACRO();
else
baz();
// 宏展开后和我们想要的就完全不一样了 直接就出错了
// do {}while(0)可以保证宏作为一个整体执行 此时就可以定义一些局部变量
- 条件编译 #ifdef等指令
-
条件编译指令
正常用的比较多的就是 #ifndef #define #endif这几个连用
也有 #defined(VAR_X)之类的 -
#error指令
如果发生错误直接中断编译过程
- #pragma 指令
- #pragma pack([n]):指示结构体和联合成员的对齐方式。
- #pragma message(“string”):在编译信息输出窗口打印自己的文
本信息。 - #pragma warning:有选择地改变编译器的警告信息行为。
- #pragma once:在头文件中添加这条指令,可以防止头文件多次
编译。
编译
真要讲编译我也是不配讲的 就我们知道这是在干嘛就行了
编译就是把.c文件变成汇编文件的过程
- 编译过程的6步
词法分析 / 语法分析 / 语义分析 / 中间代码生成 / 汇编代码生成 / 目标代码生成- 语法错误: stynax error: 缺少分号 / {}没扩住 /
- 语义错误: 类型不匹配 未定义的变量
最终的结果就是生成.S文件
- gcc的优化等级 gcc -O
可以参考
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options - 交叉编译
嵌入式开发板一般是ARM架构 然后PC是x86架构
通过交叉编译器进行程序的编译
汇编
汇编就是把汇编代码编程机器码 也就是比较熟悉的 xx.o文件了
汇编过程最终会生成以零地址为链接起始地址的可重定位目标文件
链接
把.o 文件进行组装 需要重定位 因为所有的.o文件的开头都是以0地址开头的
链接主要分为3个过程:分段组装、符号决议和重定位
-
分段组装
不太好讲 基本就是通过一个脚本把多个文件按照段组合到一起
-
符号决议
符号决议的核心就行 如果说变量/函数重名了怎么办- 不允许同时存在两个相同的强符号
初始化的全局变量、函数名默认都是强符号,未初始化的全局变量默认是弱符号
比如
- 不允许同时存在两个相同的强符号
// b.cint i; // 未初始化 是弱符号int main() {printf("%d\r\n",i); //i的值是20}// a.cint i = 20;
__attribute__关键字 可以把强符号强行转换为弱符号 attribute((weak))
- 使用弱符号的好处
-
自定义重名函数
这里在嵌入式里最常见的就是中断服务函数的弱定义了
当我们需要重新定义中断服务函数的时候 只需要保证名字很start.S的名字一致就行,链接的时候就知道链接到哪里了
-
检查该函数是否存在
// b.c
#include <stdio.h>
int global_k;
char global_i;
attribute((weak)) void func()
{
printf(“这被定义为弱符号了\r\n”);
}
int main()
{
printf(“%d\r\n”,global_k);
if(func)
func(); //调用的是强符号的函数
return 0;
}
// a.c
#include <stdio.h>
int global_k = 20;
int global_i;
void func()
{
printf(“这被定义为强符号了 fun\r\n”);
}
-
- 同样都是弱符号 谁体积大谁胜出
- 重定位
因为要把不同的文件链接到一块 所以位置就会发生变化
相关文章:

C语言八股---预处理,编译,汇编与链接篇
前言 从多个.c文件到达一个可执行文件的四步: 预处理–>编译–>汇编–>链接 预处理 预处理过程就是预处理器处理这些预处理指令(要不然编译器完全不认识),最终会生成 main.i的文件 主要做的事情有如下几点: 展开头文件展开宏条件编译删除注释添加行号等信息保留…...

平衡二叉树(AVL树)
平衡二叉树是啥我就不多说了,本篇博客只讲原理与方法。 首先引入平衡因子的概念。平衡因子(Balance Factor),以下简称bf。 bf 右子树深度 - 左子树深度。平衡结点的平衡因子可为:-1,0,1。除此…...

SpringBoot(一)--搭建架构5种方法
目录 一、⭐Idea从spring官网下载打开 2021版本idea 1.打开创建项目 2.修改pom.xml文件里的版本号 2017版本idea 二、从spring官网下载再用idea打开 三、Idea从阿里云的官网下载打开 编辑 四、Maven项目改造成springboot项目 五、从阿里云官网下载再用idea打开 Spri…...

RabbitMQ使用延迟消息
RabbitMQ使用延迟消息 1.什么情况下使用延迟消息 延迟消息适用于需要在一段时间后执行某些操作的场景,常见的有以下几类: 1.1. 订单超时取消(未支付自动取消) 场景: 用户下单后,如果 30 分钟内未付款&a…...

MyBatis-Plus 分页查询接口返回值问题剖析
在使用 MyBatis-Plus 进行分页查询时,很多开发者会遇到一个常见的问题:当分页查询接口返回值定义为 Page<T> 时,执行查询会抛出异常;而将返回值修改为 IPage<T> 时,分页查询却能正常工作。本文将从 MyBatis-Plus 的分页机制入手,详细分析这一问题的根源,并提…...

DeepLabv3+改进7:在主干网络中添加SegNext_Attention|助力涨点
🔥【DeepLabv3+改进专栏!探索语义分割新高度】 🌟 你是否在为图像分割的精度与效率发愁? 📢 本专栏重磅推出: ✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化 ✅ 即插即用模块:ASPP+升级、解码器 PS:订阅专栏提供完整代码 论文简介 近期有关移动网络设计…...

c语言笔记 内存管理之栈内存
物理内存和虚拟内存 在c语言的程序需要内存资源,用来存放变量,常量,函数代码等,不同的内容存放在不同的内存区域,不同的内存区域有着不同的特征。 c语言的每一个进程都有着一片结构相同的 虚拟内存,虚拟内…...

分布式事务的原理
文章目录 基于 XA 协议的两阶段提交(2PC)三阶段提交(3PC)TCC(Try-Confirm-Cancel)Saga 模式消息队列(可靠消息最终一致性) 分布式事务是指在分布式系统中,涉及多个节点或…...

鸿基智启:东土科技为具身智能时代构建确定性底座
人类文明的每一次跨越都伴随着工具的革新。从蒸汽机的齿轮到计算机的代码,生产力的进化始终与技术的“具身化”紧密相连。当大语言模型掀起认知革命,具身智能正以“物理实体自主决策”的双重属性重新定义工业、医疗、服务等领域的运行逻辑。在这场革命中…...

SQL29 计算用户的平均次日留存率
SQL29 计算用户的平均次日留存率 计算用户的平均次日留存率_牛客题霸_牛客网 题目:现在运营想要查看用户在某天刷题后第二天还会再来刷题的留存率。 示例:question_practice_detail -- 输入: DROP TABLE IF EXISTS question_practice_detai…...

MWC 2025 | 移远通信推出AI智能无人零售解决方案,以“动态视觉+边缘计算”引领智能零售新潮流
在无人零售市场蓬勃发展的浪潮中,自动售货机正经历着从传统机械式操作向AI视觉技术的重大跨越。 移远通信作为全球领先的物联网整体解决方案供应商,精准把握行业趋势,在2025世界移动通信大会(MWC)上宣布推出全新AI智能…...

sparkTTS window 安装
下载 Spark-TTS Go to Spark-TTS GitHubClick "Code" > "Download ZIP", then extract it. 2. 建立 Conda 环境 conda create -n sparktts python3.12 -y conda activate sparktts 3. Install Dependencies pip install -r requirements.txt In…...

数据库原理6
1.数据是信息的载体 2.数据库应用程序人员的主要职责:编写应用系统的程序模块 3.关系规范化理论主要属于数据库理论的研究范畴 4.数据库主要有检索和修改(包括插入,删除,更新)两大操作 5.概念模型又称为语义模型。…...

接口自动化入门 —— Http的请求头,请求体,响应码解析!
在接口自动化测试中,HTTP请求头、请求体和响应码是核心组成部分。理解它们的作用、格式和解析方法对于进行有效的接口测试至关重要。以下是详细解析: 1. HTTP 请求头(Request Header) 1.1 作用 请求头是客户端向服务器发送的附加…...

tcc编译器教程6 进一步学习编译gmake源代码
本文以编译gmake为例讲解如何使用tcc进行复杂一点的c代码的编译 1 简介 前面主要讲解了如何编译lua解释器,lua解释器的编译很简单也很容易理解.当然大部分c语言程序编译没那么简单,下面对前面的gmake程序进行编译. 2 gmake源码结构 首先打开之前tcc-busybox-for-win32\gmak…...

公司共享网盘怎么建立
公司共享网盘的建立,关键在于明确使用需求、选择合适的网盘服务、搭建统一的文件管理规范、做好权限分级与安全防护。尤其要强调选择合适的网盘服务这一点,如果企业规模较大,且对协同办公的需求强烈,就需要考虑支持多人实时协作、…...

【高分论文密码】AI大模型和R语言的全类型科研图形绘制,从画图、标注、改图、美化、组合、排序分解科研绘图每个步骤
在科研成果竞争日益激烈的当下,「一图胜千言」已成为高水平SCI期刊的硬性门槛——数据显示很多情况的拒稿与图表质量直接相关。科研人员普遍面临的工具效率低、设计规范缺失、多维数据呈现难等痛点,因此科研绘图已成为成果撰写中的至关重要的一个环节&am…...

深入理解Java中的static关键字及其内存原理
static是Java中实现类级共享资源的核心修饰符,它突破了对象实例化的限制,使得变量和方法能够直接与类本身绑定。这种特性让static成为构建工具类、全局配置等场景的利器,但同时也带来独特的内存管理机制需要开发者关注。 static修饰成员变量…...

linux 系统 之centos安装 docker
对于 CentOS 安装 Docker 的前置条件 首先,需要安装一些必要的软件包, 对于 CentOS 7,可以使用以下命令: sudo yum install -y yum-utils device-mapper-persistent-data lvm2添加 Docker 仓库 设置 Docker 的官方仓库。对于 …...

Python语法核心架构与核心知识点:从理论到实践
一、Python的核心设计哲学 Python以“简洁优雅”为核心理念,遵循以下原则: # Zen of Python(输入 import this 可查看) >>> import this The Zen of Python, by Tim Peters ... Simple is better than complex. Readab…...

FreeRTOS(5)内核控制函数及其他函数
FreeRTOS 提供了一些用于控制内核的 API 函数,这些 API 函数主要包含了进出临界区、开关中断、启停任务调度器等一系列用于控制内核的 API 函数。本章就来学习 FreeRTOS 的内 核控制函数。 内核控制函数 1. 函数 taskYIELD() 此函数用于请求切换任务, …...

网络DNS怎么更改?
访问速度慢或某些网站无法打开?改变网络DNS设置可能会帮助解决这些问题。本文将详细介绍如何更改网络DNS,包括更改的原因、具体步骤。 一、为什么要更改DNS? 更改DNS的原因有很多,以下是一些主要的考虑因素:某些公共DNS服务器的响应速度比…...

VIC模型有哪些优势?适用哪些范围?基于QGIS的VIC模型建模;未来气候变化模型预测;基于R语言VIC参数率定和优化
VIC模型是一个大尺度的半分布式水文模型,其设计之初就是为了模拟大流域的水文过程;它能够计算陆地-大气的能量通量,考虑土壤性质和土地利用的影响,自带有简化的湖泊/湿地模块,也能够将植被状况,…...

脏读、不可重复读,幻读的区别 mvcc及四种隔离级别
脏读:事务a还未提交更新事务b就可以看见 不可重复读:强调修改和删除,一个事务多次查询同一个表结果不同 幻读:强调新增,也是一个事务多次查询同一个表结果不同 mvcc是用来解决读写冲突的无锁并发控制 三个实现基础&…...

SpringAI介绍及本地模型使用方法
博客原文地址 前言 Spring在Java语言中一直稳居高位,与AI的洪流碰撞后也产生了一些有趣的”化学反应“,当然你要非要说碰撞属于物理反应也可以, 在经历了一系列复杂的反应方程后,Spring家族的新成员——SpringAI,就…...

numpy广播性质
一、核心规则 一维数组本质 shape (n,)的数组是无方向向量,既非严格行向量也非列向量 自动广播机制 在矩阵乘法(或np.dot())中,一维数组会自动调整维度: 前乘时视为行向量 shape (1,n)后乘时视为列向量 shape (n,1) 二、运算类型对比 假…...

Flutter_学习记录_实现列表上下拉加载 +实现加载html的数据
1. 效果图 2. 下拉加载的实现RefreshIndicator 在Flutter官方sdk中给我们提供了下拉刷新的组件RefreshIndicator。 // 显示内容列表Widget _showNewsListWidget() {if (_newsDataList.isNotEmpty) {// RefreshIndicator 来实现下拉加载的功能return RefreshIndicator(onRefr…...

基于PaddleNLP使用DeepSeek-R1搭建智能体
基于PaddleNLP使用DeepSeek-R1搭建智能体 最近在学习DeepSeek,找到了PaddleNLP星河社区大模型,跟着敲写了一遍。内容来源:DeepSeek实战训练营:从云端模型部署到应用开发 - 飞桨AI Studio星河社区-人工智能学习与实训社区 本项目基…...

『PostgreSQL』PGSQL备份与还原实操指南
📣读完这篇文章里你能收获到 了解逻辑备份与物理备份的区别及适用场景🔍。掌握全库、指定库、指定表备份还原的命令及参数📝。学会如何根据业务需求选择合适的备份策略📊。熟悉常见备份还原问题的排查与解决方法🔧。 …...

基于单片机的智慧农业大棚系统(论文+源码)
1系统整体设计 经过上述的方案分析,采用STM32单片机为核心,结合串口通信模块,温湿度传感器,光照传感器,土壤湿度传感器,LED灯等硬件设备来构成整个控制系统。系统可以实现环境的温湿度检测,土壤…...