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

C语言八股---预处理,编译,汇编与链接篇

前言

  从多个.c文件到达一个可执行文件的四步:
  预处理–>编译–>汇编–>链接

预处理

在这里插入图片描述

  预处理过程就是预处理器处理这些预处理指令(要不然编译器完全不认识),最终会生成 main.i的文件
主要做的事情有如下几点:

  • 展开头文件
  • 展开宏
  • 条件编译
  • 删除注释
  • 添加行号等信息
  • 保留parama预处理指令
  1. 头文件展开—#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 多次包含是不会的增加可执行文件的体积的
      同样的要注意:声明不会增加可执行文件的体积
  2. 宏展开#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)可以保证宏作为一个整体执行 此时就可以定义一些局部变量
  1. 条件编译 #ifdef等指令
  • 条件编译指令
    在这里插入图片描述

    正常用的比较多的就是 #ifndef #define #endif这几个连用
    也有 #defined(VAR_X)之类的

  • #error指令
    如果发生错误直接中断编译过程
    在这里插入图片描述

  1. #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树)

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

SpringBoot(一)--搭建架构5种方法

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

RabbitMQ使用延迟消息

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

MyBatis-Plus 分页查询接口返回值问题剖析

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

DeepLabv3+改进7:在主干网络中添加SegNext_Attention|助力涨点

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

c语言笔记 内存管理之栈内存

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

分布式事务的原理

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

鸿基智启:东土科技为具身智能时代构建确定性底座

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

SQL29 计算用户的平均次日留存率

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

MWC 2025 | 移远通信推出AI智能无人零售解决方案,以“动态视觉+边缘计算”引领智能零售新潮流

在无人零售市场蓬勃发展的浪潮中&#xff0c;自动售货机正经历着从传统机械式操作向AI视觉技术的重大跨越。 移远通信作为全球领先的物联网整体解决方案供应商&#xff0c;精准把握行业趋势&#xff0c;在2025世界移动通信大会&#xff08;MWC&#xff09;上宣布推出全新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.数据库应用程序人员的主要职责&#xff1a;编写应用系统的程序模块 3.关系规范化理论主要属于数据库理论的研究范畴 4.数据库主要有检索和修改&#xff08;包括插入&#xff0c;删除&#xff0c;更新&#xff09;两大操作 5.概念模型又称为语义模型。…...

接口自动化入门 —— Http的请求头,请求体,响应码解析!

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

tcc编译器教程6 进一步学习编译gmake源代码

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

公司共享网盘怎么建立

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

【高分论文密码】AI大模型和R语言的全类型科研图形绘制,从画图、标注、改图、美化、组合、排序分解科研绘图每个步骤

在科研成果竞争日益激烈的当下&#xff0c;「一图胜千言」已成为高水平SCI期刊的硬性门槛——数据显示很多情况的拒稿与图表质量直接相关。科研人员普遍面临的工具效率低、设计规范缺失、多维数据呈现难等痛点&#xff0c;因此科研绘图已成为成果撰写中的至关重要的一个环节&am…...

深入理解Java中的static关键字及其内存原理

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

linux 系统 之centos安装 docker

对于 CentOS 安装 Docker 的前置条件 首先&#xff0c;需要安装一些必要的软件包&#xff0c; 对于 CentOS 7&#xff0c;可以使用以下命令&#xff1a; sudo yum install -y yum-utils device-mapper-persistent-data lvm2添加 Docker 仓库 设置 Docker 的官方仓库。对于 …...

Python语法核心架构与核心知识点:从理论到实践

一、Python的核心设计哲学 Python以“简洁优雅”为核心理念&#xff0c;遵循以下原则&#xff1a; # Zen of Python&#xff08;输入 import this 可查看&#xff09; >>> import this The Zen of Python, by Tim Peters ... Simple is better than complex. Readab…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...