重头开始嵌入式第十二天(预处理和指针)

news2024/11/14 5:06:17

预处理

 

在 C 语言中,预处理是指在编译之前由预处理器对源代码进行的一些处理操作。
 
主要包括以下几个方面:
 
1. 宏定义:使用  #define  指令定义一个标识符来代表一个常量值、表达式或一段代码。
例如: #define PI 3.14159 
2. 文件包含:使用  #include  指令将另一个文件的内容插入到当前位置。
例如: #include <stdio.h> 
3. 条件编译:根据特定的条件决定是否编译某段代码。
例如: #ifdef DEBUG  、  #ifndef  等
 
预处理指令以  #  开头,预处理阶段会对这些指令进行处理,并将处理后的结果交给编译器进行编译。预处理的作用在于增强代码的可移植性、可维护性和灵活性。 

gcc编译流程

 

2dc791a3c73b4c769a2660040b93ca35.png

 

GCC(GNU Compiler Collection)的编译流程可以更详细地描述如下:

 

1. 预处理(Preprocessing):

 

- 输入: .c  或  .cpp  源代码文件

- 操作:

- 读取源代码文件。

- 处理以  #  开头的预处理指令,如:

- 宏定义( #define ):将宏名替换为宏值。

- 文件包含( #include ):把指定文件的内容插入到当前位置。

- 条件编译( #ifdef 、 #ifndef 、 #if  等):根据条件决定是否包含某些代码段。

- 输出: .i  预处理后的中间文件

2. 编译(Compilation):

 

- 输入: .i  文件

- 操作:

- 对预处理后的代码进行词法分析、语法分析和语义分析。

- 生成与特定 CPU 架构相关的汇编语言代码。

- 输出: .s  汇编语言文件

3. 汇编(Assembly):

 

- 输入: .s  文件

- 操作:

- 汇编器将汇编语言代码转换为机器语言的目标文件。

- 目标文件包含了代码段、数据段以及符号表等信息。

- 输出: .o  目标文件

4. 链接(Linking):

 

- 输入:多个  .o  目标文件和库文件

- 操作:

- 链接器把多个目标文件以及所需的库文件(静态库或动态库)组合在一起。

- 解决符号引用和重定位问题,确定函数和变量的最终地址。

- 输出:可执行文件(在类 Unix 系统中通常没有扩展名,在 Windows 系统中通常为  .exe )或共享库(如  .so  或  .dll )

 

在整个编译流程中,可以通过各种命令行选项来控制编译的细节,例如优化级别、调试信息的生成、指定目标架构等。此外,还可以使用一些工具来辅助分析和理解编译过程中的中间结果,如  objdump  用于查看目标文件的内容, readelf  用于分析 ELF 格式的文件等。

宏定义

宏定义是 C 语言中一种预处理指令,用于为标识符指定一个替换文本。

 

它有两种常见形式:

 

1. 对象式宏定义:

使用  #define  指令为一个标识符定义一个常量值或表达式。例如: #define PI 3.14159  ,在后续代码中使用  PI  就相当于使用  3.14159  。

2. 函数式宏定义:

类似于函数,但在预处理阶段进行文本替换。例如: #define SQUARE(x) ((x) * (x))  ,使用时如  SQUARE(5)  会被替换为  ((5) * (5))  。

宏定义还可以是一段代码,但是必须在一行,不在一行要加续行符/

 

宏定义的优点包括:

 

1. 增强代码的可读性和可维护性,使用有意义的标识符代替常量或复杂表达式。

2. 提高代码的可移植性,方便在不同环境中修改常量值。

 

但也存在一些潜在问题:

 

1. 可能导致代码可读性降低,尤其是复杂的宏定义。

2. 由于是简单的文本替换,可能会出现意外的结果,例如参数的多次计算。

 

宏定义在以下场景中可能会出现意外的结果:

 

1. 参数的副作用:如果宏定义的参数在表达式中有副作用(例如自增、自减操作),可能会导致不符合预期的结果。因为宏只是简单的文本替换,可能会多次计算参数,从而多次触发副作用。

 

例如:

#define FUNC(x) (x + x)

int a = 5;

int b = FUNC(a++); // 这里 a 会被增加两次,而不是预期的一次

 

2. 运算优先级问题:宏替换后的表达式可能会改变原本的运算优先级,导致计算结果与预期不同。

 

例如:

#define MULTIPLY(a, b) (a * b)

int x = 1 + MULTIPLY(2, 3); // 替换后变成 1 + (2 * 3),而不是预期的先计算乘法 (1 + 2) * 3

 

3. 作用域和可见性:宏定义在整个文件中都是可见的,可能会与局部变量或同名的函数产生冲突。

4. 字符串连接问题:如果宏定义中包含字符串连接操作,可能会因为没有考虑到字符串的边界和分隔符而产生意外。

 

例如:

#define CONCAT(a, b) a##b

char* str = CONCAT("hello, ", "world"); // 期望得到 "hello, world",但实际可能因为没有空格而不符合预期

在使用宏定义时,需要特别小心这些情况,以避免出现意外的错误结果。

 

在 C 语言中,“文件包含”是一种预处理指令,用于将一个指定的文件内容插入到当前的源代码中。

 

文件包含通过  #include  指令来实现,主要有以下两种形式:

 

1.  #include <文件名>  :用于包含系统提供的头文件,编译器会按照特定的路径(通常是系统默认的包含路径)去查找指定的文件。

 

例如: #include <stdio.h>  ,用于包含标准输入输出头文件。

 

2.  #include "文件名"  :用于包含用户自定义的头文件,编译器首先在当前源文件所在的目录中查找,如果未找到,再按照系统指定的路径进行查找。

 

例如: #include "myheader.h"  ,假设  myheader.h  是用户自己编写的头文件。

 

文件包含的主要作用是:

 

1. 提高代码的复用性:可以将一些常用的函数声明、宏定义、类型定义等放在一个单独的头文件中,然后在多个源文件中包含该头文件,避免重复编写相同的代码。

2. 增强代码的组织结构和可读性:将相关的定义和声明集中在一个头文件中,使代码更易于理解和维护。

 

在使用文件包含时,需要注意避免重复包含同一个文件,这可能会导致编译错误或产生不符合预期的结果。通常可以使用条件编译指令(如  #ifndef  、  #define  、  #endif  )来防止重复包含。

条件编译

在 C 语言中,条件编译是指根据预定义的条件来决定是否编译某段代码。

 

常见的条件编译指令有:

 

1.  #ifdef  (如果已定义):如果指定的标识符已经被  #define  定义过,则编译后续的代码段。

 

例如:

#define DEBUG

#ifdef DEBUG

    printf("This is a debug message.\n");

#endif

 

2.  #ifndef  (如果未定义):如果指定的标识符未被  #define  定义过,则编译后续的代码段。

例如:

#ifndef DEBUG

    printf("Debug mode is not enabled.\n");

#endif

 

3.  #if  (如果表达式为真):根据指定的表达式的值来决定是否编译后续的代码段。表达式通常是常量表达式。

 

例如:

#if 1

    printf("This code will be compiled.\n");

#endif



#if 0

    printf("This code will not be compiled.\n");

#endif

 

条件编译的主要作用包括:

 

1. 方便调试:在开发过程中,可以通过定义调试标识符来包含调试输出的代码,而在发布版本中取消这些调试输出。

2. 适应不同的平台和环境:根据不同的操作系统、编译器版本、硬件架构等条件,编译不同的代码。

3. 提高代码的可维护性和可移植性:可以将与特定条件相关的代码分离出来,便于管理和修改。

 

指针

 

在 C 语言中,指针是一个非常重要且强大的概念。

 

定义:指针是一种用于存储变量内存地址的变量。它能够让程序直接操作内存地址,从而实现更高效和灵活的数据处理。

 

例如,定义一个指向整型的指针可以这样写: int *ptr;  这里的  *  表示这是一个指针类型的变量。

 

概念:

 

1. 内存地址:计算机内存被划分为一系列的存储单元,每个单元都有一个唯一的地址,就像房间的门牌号一样。

2. 间接访问:通过指针,可以间接地访问和操作指针所指向的内存位置中的数据。

 

引用(使用):

 

1. 取地址操作:要让指针指向某个变量,需要使用取地址运算符  &  。比如,有一个整型变量  int num = 10;  ,可以通过  int *ptr = &num;  使  ptr  指向  num  。

2. 解引用操作:使用解引用运算符  *  来访问指针所指向的变量的值或对其进行修改。例如,如果  ptr  指向了  num  ,那么  *ptr = 20;  就会把  num  的值修改为 20 。

3. 指针运算:在指针上可以进行一些有限的运算,比如加减一个整数,这通常用于在数组等连续内存区域中移动指针。

4. 指针与数组:数组名在很多情况下会被当作指向数组首元素的指针来使用,通过指针可以方便地遍历数组。

 

指针在 C 语言中的应用广泛,但由于其直接操作内存,使用不当可能会导致严重的错误,如内存访问越界、内存泄漏等。因此,在使用指针时需要格外小心,确保代码的正确性和稳定性。

 

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亚马逊澳大利亚站 带绳窗帘认证步骤

带绳窗帘是一种室内用窗帘&#xff0c;可通过一根吊绳控制升降。此类商品包括但不限于蜂窝帘、水平百叶帘、百褶帘、卷式百叶帘、卷帘、透光帘、罗马帘、帘杆&#xff08;包括使用帘杆的商品&#xff0c;如带帘杆的窗帘和布帘&#xff09;、面板轨道和垂直百叶帘。 我们的带绳窗…

vue 开发工具 Hbuilder 简介及应用

一、简介 HBuilderX 是一款流行的前端开发工具&#xff0c;由DCloud公司开发。它支持多种编程语言&#xff0c;如HTML、CSS、JavaScript、Vue、UniApp等&#xff0c;非常适合用来开发Web应用、移动端应用和跨平台应用。 官网地址&#xff1a;https://www.dcloud.io/hbuilderx.…

ShardingSphere 内核工作原理

文章目录 内核工作原理配置管控SQL Parser: SQL解析引擎SQL Router- SQL 路由引擎SQL Rewriter : SQL 优化引擎SQL Executor &#xff1a; SQL执行引擎Result Merger&#xff1a; 结果归并 内核工作原理 ShardingSphere的整体架构图是这样的&#xff1a; 配置管控 在进入Shar…

火车站NTP电子钟,自动授时,保证时间精准

在现代交通体系中&#xff0c;火车站作为重要的交通枢纽&#xff0c;每天承载着大量乘客的出行需求。为了确保列车运行的准时性和乘客信息的准确性&#xff0c;火车站NTP电子钟成为不可或缺的一部分。本文将详细介绍火车站NTP电子钟的特点及其在不同场景中的应用优势。 一、火车…

【STL】String的底层构造

1.String类对象的构造&#xff08;后面有每一个接口的实现&#xff09; #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include<iostream> #include<assert.h> using namespace std;namespace bit {class string{public:typedef char* iterator;typedef const…

字节实习面试

1.比左边的数都小&#xff0c;比右边的数都大 public class Test3 {/*** 从左往右找当前位置往左最小的* 从右往左遍历找当前位置往右最大的* 比较* param args*/public static void main(String[] args) { // int[] arr new int[]{9,8,7,3,4,2,1};int[] arr new int[…

【第十届泰迪杯数据挖掘挑战赛A题害虫识别】-农田害虫检测识别-高精度完整更新

农田害虫检测识别项目-高精度完整版 一、说明&#xff1a; 该版本为基于泰迪杯完整害虫数据重新制作数据集、优化增强数据集、重新进行模型训练&#xff0c;达到高精度、高召回率的最优模型代码。包含论文、最优模型文件以及相关文件、原始数据集、训练数据集XML版、增强扩充…

五款超好用的报表软件推荐,其中一款竟然完全免费

与以往需要通过繁琐的数据表格和复杂的数字分析不同&#xff0c;可视化报表通过表格、图表和图形&#xff0c;将数据以更加直观的方式呈现出来&#xff0c;使得原本繁杂无序的数据变得清晰易懂。无论是管理层的决策分析&#xff0c;还是一线员工的日常工作&#xff0c;可视化报…

vs+qt一些问题

一直遇到的两个问题&#xff0c;今天解决了 1、 因为前后端分离&#xff0c;前端写完了&#xff0c;后端还在一直修改&#xff0c;但是每次都是单独打开的后端的sln&#xff0c;所以会出现这个&#xff0c;把前端的模块删掉就好了。 2、打开vs项目&#xff0c;很多报错&#…

怎么在视频上加文字?归纳了简单好用的方法

怎么在视频上加文字&#xff1f;在数字媒体制作中&#xff0c;为视频添加文字是一种常见的需求。无论是为了提供字幕、注释、标题还是视觉效果&#xff0c;文字元素都能增强视频的传达力和观赏性。因此&#xff0c;今天本文将介绍四种简单好用的方法&#xff0c;帮助你轻松地在…

硬盘数据恢复:所需时长、全面指南及注意事项

在数字化时代&#xff0c;硬盘作为我们存储重要数据的核心设备&#xff0c;其重要性不言而喻。然而&#xff0c;由于各种原因&#xff0c;如误删除、格式化、硬盘故障等&#xff0c;我们时常面临数据丢失的困境。数据恢复不仅关乎个人隐私和信息安全&#xff0c;更可能影响到我…

Platform Designer各模块(2)

1.On-Chip Memory&#xff08;存储器&#xff09; (RAM or ROM) Intel FPGA IP RAM&#xff1a;主存&#xff0c;与CPU直接交换数据的内部存储器&#xff0c;可读可写&#xff0c;断电不丢失数据&#xff1b; ROM&#xff1a;只能读取数据&#xff0c;断电丢失数据。 2.Syste…

如何利用matlab将现有的地基雷达回波数据调制为机载雷达回波数据???

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

Bug太多,苹果手机升级到18.1后怎么降级

最近苹果公司发布了iOS 18.1开发者测试版和macOS Sequoia 15.1的开发者测试版&#xff0c;此次发布的iOS18.1开发者测试版本苹果给其带来了两个重要的新功能。 1、通话录音功能 现在只要拨打或者是接听电话&#xff0c;界面左上角就会出现通话录音按键&#xff0c;点击即可开启…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第五篇 文件系统构建篇-第七十四章 buildroot构建文件系统

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

老照片修复软件分享3款!码住一些实用的方法!

在数字时代&#xff0c;老照片不仅是时间的印记&#xff0c;更是我们珍贵的记忆载体。然而&#xff0c;随着时间的流逝&#xff0c;这些照片往往会变得模糊、褪色甚至破损。幸运的是&#xff0c;现代科技的发展为我们提供了多种老照片修复软件&#xff0c;让我们能够轻松恢复这…

docker 发布geoserver服务添加字体

1. 创建容器时可直接挂载到系统字体库 2. 已发布的容器挂载字体目录 关闭docker服务 &#xff1a; systemctl stop docker.socket 修改config.v2.json :位置在 cd /var/lib/docker/containers/容器id 重新启动docker服务&#xff1a;systemctl start docker

【启明智显方案分享】6.86寸高清显示屏音频效果器解决方案

一、项目概述 本方案旨在设计一款集成6.86寸高清触摸显示屏的音频效果器&#xff0c;通过HMI&#xff08;Human-Machine Interface&#xff09;芯片Model 4驱动&#xff0c;实现高清晰度的视觉交互。该设备不仅支持音乐、麦克风及温响音量的精细控制&#xff0c;还内置丰富的预…

vue信息列表实现点击加载更多陆续显示后面数据

原理&#xff1a;在点击加载更多后进行判断&#xff1a; if (this.currentPage < this.totalPages - 1) {this.currentPage;} 如果当前页码小于总页码就让当前页码1&#xff0c;通过计算属性动态更新开始和结束值&#xff0c;从而当前页面展示更多数据&#xff1a; paginate…

WPF使用Iconfont字符串的操作方法

1.下载字体库文件 https://www.bootcss.com/p/font-awesome/ 2.放入Fonts文件夹下 3.文件属性修改为 资源 3.引入 FontFamily字体类型 <Setter Property"FontFamily" Value"../../CommonInMy/Fonts/#iconfont"/>注意这里的地址请填写绝对地址&…