【机器学习智能硬件开发全解】(九)—— 政安晨:通过ARM-Linux掌握基本技能【C语言程序的预处理过程】

政安晨的个人主页政安晨

欢迎 👍点赞✍评论⭐收藏

收录专栏机器学习智能硬件开发全解

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!

C语言程序的预处理过程是在编译阶段之前进行的,主要包括以下几个步骤

  1. 去除注释预处理器会将程序中的注释去除,不会传递给编译器。

  2. 头文件包含预处理器会根据程序中的#include指令,将对应的头文件内容插入到程序中。这样可以方便地使用已经定义好的变量、函数等。

  3. 宏替换预处理器会根据程序中定义的宏,将宏的引用替换为相应的内容。例如,将宏定义的常数替换为实际的数值,将宏定义的函数调用替换为函数体。

  4. 条件编译预处理器会根据程序中的条件编译指令,决定是否编译相应的代码段例如,根据条件判断是否编译某个平台的特定代码。

  5. 处理特殊指令预处理器还可以处理特殊的预处理指令,例如#define#ifdef等,对程序代码进行进一步的处理。

预处理器处理完程序后,生成的预处理文件会被传递给编译器进行编译。


预处理过程

为了方便编程,编译器一般为开发人员提供一些预处理命令,使用#标识。

我们常见的预处理命令如下:

● 头文件包含:#include。

● 定义一个宏:#define。

● 条件编译:#if、#else、#endif。

● 编译控制:#pragma。

编译器提供的这些预处理命令,大大方便了程序的编写:

通过头文件包含可以实现模块化编程;

使用宏可以定义一个常量,提高程序的可读性;

通过条件编译可以让代码兼容不同的处理器架构和平台,以最大限度地复用公用代码。

通过#pragma预处理命令可以设定编译器的状态,指示编译器完成一些特定的动作

#pragma pack([n]):指示结构体和联合成员的对齐方式。

#pragma message("string"):在编译信息输出窗口打印自己的文本信息。

#pragma warning:有选择地改变编译器的警告信息行为。

#pragma once:在头文件中添加这条指令,可以防止头文件多次编译。

预处理过程,其实就是在编译源程序之前,先处理源文件中的各种预处理命令

编译器是不认识预处理指令的,在编译之前不先把这些预处理命令处理掉,编译器就会报错。

预处理主要包括以下操作

头文件展开:将#include包含的头文件内容展开到当前位置。

宏展开:展开所有的宏定义,并删除#define。

条件编译:根据宏定义条件,选择要参与编译的分支代码,其余的分支丢弃。

● 删除注释。

添加行号和文件名标识:编译过程中根据需要可以显示这些信息。

保留#pragma命令:该命令会在程序编译时指示编译器执行一些特定行为。

一个源程序在预处理前后有什么变化呢?

重写前面的C程序

我们写了一个测试程序,分别使用预处理命令去定义一些宏和条件编译。

咱们在上一篇文章中代码的基础上修改一下)上一篇文章:

【机器学习智能硬件开发全解】(八)—— 政安晨:通过ARM-Linux掌握基本技能【C语言程序的编译/链接/安装运行】icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136805174对上一章中的main.c文件重写如下:

#include <stdio.h>
#include "sub.h"

#define PI 3.14

int global_val = 1;
int uninit_val;

void platform_init()
{
#ifdef ARM
        printf("ARM platform init ... \n");
#else
        printf("X86 platform init ... \n");
#endif

        return;
}

#pragma pack(2)
#pragma message("build main.c ...\n")

float f = PI;

int main(void)
{
        platform_init();

        int a, b;
        static int local_val = 2;
        static int uninit_local_val;

        a = add(2, 3);
        b = sub(5, 4);

        printf("a = %d\n", a);
        printf("b = %d\n", b);

        return 0;
}

对上面的C程序只作预处理操作,不编译,将输出的信息重定向到main.i文件。

## 在命令行控制台执行下面命令,并将预编译的信息写入main.i文件中
arm-linux-gnueabihf-gcc -E main.c > main.i

## 查看预编译文件
cat main.i

main.i中的部分信息如下:

# 0 "main.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/arm-linux-gnueabihf/include/stdc-predef.h" 1 3
# 0 "<command-line>" 2
# 1 "main.c"
# 1 "/usr/arm-linux-gnueabihf/include/stdio.h" 1 3
# 27 "/usr/arm-linux-gnueabihf/include/stdio.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/libc-header-start.h" 1 3
# 33 "/usr/arm-linux-gnueabihf/include/bits/libc-header-start.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/features.h" 1 3
# 392 "/usr/arm-linux-gnueabihf/include/features.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/features-time64.h" 1 3
# 20 "/usr/arm-linux-gnueabihf/include/features-time64.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/wordsize.h" 1 3
# 21 "/usr/arm-linux-gnueabihf/include/features-time64.h" 2 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/timesize.h" 1 3
# 22 "/usr/arm-linux-gnueabihf/include/features-time64.h" 2 3
# 393 "/usr/arm-linux-gnueabihf/include/features.h" 2 3
# 489 "/usr/arm-linux-gnueabihf/include/features.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 1 3
# 559 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/wordsize.h" 1 3
# 560 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 2 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/long-double.h" 1 3
# 561 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 2 3
# 490 "/usr/arm-linux-gnueabihf/include/features.h" 2 3
# 513 "/usr/arm-linux-gnueabihf/include/features.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/gnu/stubs.h" 1 3
# 10 "/usr/arm-linux-gnueabihf/include/gnu/stubs.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/gnu/stubs-hard.h" 1 3
# 11 "/usr/arm-linux-gnueabihf/include/gnu/stubs.h" 2 3
# 514 "/usr/arm-linux-gnueabihf/include/features.h" 2 3
# 34 "/usr/arm-linux-gnueabihf/include/bits/libc-header-start.h" 2 3
# 28 "/usr/arm-linux-gnueabihf/include/stdio.h" 2 3

......
......
......

# 2 "main.c" 2
# 1 "sub.h" 1

# 1 "sub.h"
int add(int nCountA, int nCountB);
int sub(int nCountA, int nCountB);
# 3 "main.c" 2



int global_val = 1;
int uninit_val;

void platform_init()
{



 printf("X86 platform init ... \n");


 return;
}

#pragma pack(2)

# 21 "main.c"
#pragma message("build main.c ...\n");
float f = 3.14;

int main(void)
{
 int a, b;
 static int local_val = 2;
 static int uninit_local_val;

 a = add(2, 3);
 b = sub(5, 4);

 printf("a = %d\n", a);
 printf("b = %d\n", b);

 return 0;
}

通过预处理前后源文件的变化对比,我们可以看到

当预处理器遇到#include命令时,会直接将包含的头文件内容展开,并删除#include;

当遇到#define宏时,执行同样的操作。

当遇到条件编译指令时,会根据开发者定义的宏标记,选择要参与编译的代码部分,其余部分删除,经过预处理后,#pragma保留,指示编译器在后续的编译阶段执行一些特定的操作。

继续编译预处理后的C程序,在编译信息提示窗口里,我们会看到自己添加的编译提示信息。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/470956.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5中各类型的英文名称缩写(直接用于文件前缀)

真正开发项目时用到的素材文件是相当巨量的&#xff0c;在资产中查找时由于不区分文件夹&#xff0c;因此查找是比较头疼的&#xff0c;所以很多同类型的文件名命名时要加入缩写。 本文提供初学者内容包中的缩写&#xff0c;并会在此后陆续加入自定义的缩写&#xff08;本部分…

css实现的3D立体视觉效果鸡蛋动画特效

这是一个基于纯css实现的3D立体视觉效果鸡蛋动画特效&#xff0c;喜欢的朋友可以拿来使用演示动态效果 css实现的3D立体视觉效果鸡蛋动画特效

Intellij IDEA--解决git的master分支不能force push的问题

原文网址&#xff1a;Intellij IDEA--解决git的master分支不能force push的问题-CSDN博客 简介 本文介绍Intellij IDEA怎样解决git的master分支不能force push的问题。 问题复现 解决方法 把保护分支里删除master

[AIGC] MySQL与PostgreSQL:两种流行的数据库系统的对比

数据库是存储和查询数据的重要工具。在选择数据库时&#xff0c;两个经常被考虑的选项都是开源的&#xff1a;MySQL和PostgreSQL。这两个数据库都与许多应用程序一起使用&#xff0c;但它们在某些方面存在显著的不同。在本文中&#xff0c;我们将比较MySQL和PostgreSQL的一些关…

路由器怎么做端口映射

路由器在网络中起到了连接不同设备和提供网络服务的重要作用。端口映射是一项常见的操作&#xff0c;它允许外部网络中的设备通过路由器访问内部网络中的设备。我们将介绍如何在路由器上进行端口映射的设置。 理解端口映射 在开始操作之前&#xff0c;我们需要了解一些基本概念…

Android Studio配置buildTypes{}后,gradle中Tasks列表不显示assembleRelease。

打开Files → Settings → Experimental 取消选中 "Do not build Gradle task list during Grafle sync"

Redis之缓存穿透、缓存雪崩、缓存击穿

Redis之缓存穿透、缓存雪崩、缓存击穿 什么是缓存穿透&#xff1f; 如果有人故意将请求打到未缓存的数据上&#xff0c;会对数据库造成巨大的压力 如何解决&#xff1f; 做好参数校验&#xff0c;比如请求的id不能<0&#xff0c;在访问数据库前就把这些异常访问拦截了 缓…

幼犬狗粮和成年犬狗粮该怎么挑选?

亲爱的狗友们&#xff0c;我们都知道&#xff0c;给狗狗选择适合的狗粮是非常重要的。那么&#xff0c;面对市面上琳琅满目的幼犬狗粮和成年犬狗粮&#xff0c;我们该如何挑选呢&#xff1f;别担心&#xff0c;接下来就让我来给大家支支招。 &#x1f436; 幼犬狗粮挑选篇 &…

【测试开发学习流程】MySQL函数运算(中)(下)

前言&#xff1a; 这些天还要搞毕业论文&#xff0c;东西少了点&#xff0c;大家将就看看QWQ 目录 1 MySQL的数据处理函数 1.1 文本处理函数 1.2 日期与时间函数 1.3 数值处理函数 1.4 系统函数 2 聚集运算 2.1 聚集函数 2.2 流程函数 1 MySQL的数据处理函数 MySQL支…

机器学习 - 训练模型

接着这一篇博客做进一步说明&#xff1a; 机器学习 - 选择模型 为了解决测试和预测之间的差距&#xff0c;可以通过更新 internal parameters, the weights set randomly use nn.Parameter() and bias set randomly use torch.randn(). Much of the time you won’t know what…

Unity 实现双屏或多屏内容展示

在某些应用场景&#xff0c;一个应用可能需要使用多个显示器显示。 Unity支持最多8个不同显示器同时显示应用程序中八个摄像头的视图&#xff0c;如下图&#xff1a; 具体实现如下&#xff1a; 1、在Hiearchy面板上点击鼠标右键->Camera,创建多一个Camera,如图&#xff1a…

postman 用上一个请求的响应体中的字段设置下一个请求的请求参数

文章目录 IntroPostman usagePre-request ScriptTests javascripts API Intro 这一切都是为了增加自动化动作所占的比例&#xff08;减少人手工操作复制粘贴可能会造成的错误&#xff09;。 Postman usage 最常用的&#xff1a;选HTTP方法类型、写URL&#xff0c;在Headers中…

EvaRichter勒索病毒来袭

目前勒索病毒仍然是全球最大的威胁&#xff0c;最近一年针对企业的勒索病毒攻击越来越多&#xff0c;大部分勒索病毒是无法解密的&#xff0c;并且不断有新型的勒索病毒出现&#xff0c;各企业一定要保持高度的重视&#xff0c;马上放假了&#xff0c;一款新型勒索病毒来袭....…

【Linux】编译器-gcc/g++的使用(预处理、编译、汇编、连接)

目录 01.预处理&#xff08;宏替换&#xff09; 02.编译&#xff08;生成汇编&#xff09; 03.汇编&#xff08;生成机器可识别码&#xff09; 04.连接&#xff08;生成可执行文件或库文件&#xff09; 05.选项 编译器在编译代码时包含以下四个步骤&#xff1a;1.预处理 2…

数据结构(三)复杂度的深层次剖析

之前发布了数据结构&#xff08;一&#xff09;&#xff0c;很多同学反响不够清晰&#xff0c;那今天就发一篇对复杂度专题的博客&#xff0c;希望对大家理解复杂度提供一些帮助。 时间复杂度 我们先来一个理解一个复杂度&#xff0c;二分查找的复杂度&#xff08;之前写过二…

app调用系统接口示意图

1.linux下app不能直接访问内核。 用户态和内核态 2. 系统调用是应用程序和系统内核之间的接口。 &#xff08;1&#xff09;app访问内核通过调用glibc中的系统调用接口&#xff08;open&#xff08;&#xff09;、read&#xff08;&#xff09;、write&#xff08;&#xff09;…

html5cssjs代码 024 响应式布局示例

html5&css&js代码 024 响应式布局示例 一、代码二、解释 该HTML代码重点在于构建一个带有响应式设计的两栏布局网页&#xff0c;包含页头、导航条、主要内容区&#xff08;左右两列&#xff09;和底部区域&#xff0c;并运用CSS样式设置页面元素的布局、颜色、字体、间…

C++特性三:多态---案例三(电脑组装)

案例描述&#xff1a; 电脑主要组成部件为 CPU&#xff08;用于计算&#xff09;&#xff0c;显卡&#xff08;用于显示&#xff09;&#xff0c;内存条&#xff08;用于存储&#xff09; 将每个零件封装出抽象基类&#xff0c;并且提供不同的厂商生产不同的零件&#xff0c;例…

PwnLab靶场PHP伪协议OSCP推荐代码审计命令劫持命令注入

下载链接&#xff1a;PwnLab: init ~ VulnHub 安装&#xff1a; 打开vxbox直接选择导入虚拟电脑即可 正文&#xff1a; 先用nmap扫描靶机ip nmap -sn 192.168.1.1/24 获取到靶机ip后&#xff0c;对靶机的端口进行扫描&#xff0c;并把结果输出到PwnLab文件夹下&#xff0c;命名…

杰发科技AC7801——Keil编译的Hex大小如何计算

编译结果是Keil里面前三个数据的总和&#xff1a; 即CodeRoDataRWData的总和。 通过ATCLinkTool工具查看内存&#xff0c;发现最后一个字节正好是5328 注意读内存数据时候需要强转成32位&#xff0c;加1000的 增加1024的地址只需要加256即可