嵌入式学习之路 13(C语言基础学习——预处理命令)

编程流程

在进行程序开发时,通常遵循编辑源代码、编译、运行和调试这几个主要步骤。

  1. 编辑源代码:使用文本编辑器创建或修改程序的源代码,这是整个编程过程的起点。
  2. 编译:将源代码转换为可执行文件的关键步骤。
    • 预处理:这是编译过程的前置阶段,通过执行预处理命令来处理源代码。预处理命令包括 #include (包含头文件)、#define (定义宏)等。例如,#include <stdio.h> 会将 stdio.h 头文件的内容插入到当前代码位置。预处理最终生成一个只包含纯 C 语言代码的中间文件。
      • 指令:gcc -E main.c -o main.i ,此命令执行预处理操作,并将结果输出到 main.i 文件。
    • 编译:对经过预处理的代码进行语法检查,并将其转换为汇编代码。这一阶段会检查代码的语法错误,如遗漏的分号、错误的变量使用等。
      • 指令:gcc -S main.c -o main.s ,生成汇编代码文件 main.s 。
    • 汇编:将汇编代码转换为机器代码,生成目标文件(通常以 .o 为扩展名)。
      • 指令:gcc -c main.s -o main.o ,生成目标文件 main.o 。
  3. 链接:将多个目标文件以及所需的库文件链接在一起,生成最终的可执行文件。例如,如果程序中使用了外部库函数,链接阶段会将这些函数的实现与当前代码连接起来,确保程序能够正常运行。
  4. 运行:执行生成的可执行文件,观察程序的输出结果。
  5. 调试:如果程序运行结果不符合预期,通过调试工具来查找和修复代码中的错误。


预处理

        预处理不属于 C 语言的核心部分,其主要作用是进行文本替换,包括宏定义、文件包含和条件编译。

宏定义

  • 语法形式:#define 标识符 字符串 或 #define 宏名 宏值
    • 例如:#define N 10 ,在代码中出现的 N 都会被替换为 10 。
    • 注意事项:预处理命令均以 # 开头,宏名的命名规则与普通标识符相同,通常写成大写以作区分。
  • 宏名的作用域:从定义处开始,到 #undef 结束。#undef 宏名 用于结束宏名的作用范围。需要注意的是,宏名的作用仅在预处理阶段发挥作用。
#include <stdio.h>#define ARRAY_SIZE 10int main()
{int a[ARRAY_SIZE] = {1,2,3,4,5,6,7,8,9,10};int i;for ( i=0; i<ARRAY_SIZE; i++ ){printf("%d ",a[i]);}printf("\n");return 0;
}

宏的应用

  • 提高代码的可读性。
  • 方便代码修改,实现一改全改。

带参宏定义(宏函数)

  • 语法:#define 宏名(参数) 宏值
    • 例如:#define ADD(a,b) a+b
    • 注意:虽然被称为宏函数,但它与真正的函数有本质区别。
  • 处理阶段不同:宏定义发生在预处理阶段,而函数在编译阶段。
  • 使用方式不同:宏在预处理阶段通过文本原样替换完成使用,其参数仅用于文本替换,不进行语法检查;函数在调用时使用,参数具有类型,编译阶段会进行类型检查。
  • 应用场景:对于一些短小的代码(通常不超过 5 行),可考虑写成带参宏。
#include <stdio.h>#define ADD(a,b) a+b
#define SUB(a,b) a-b
#define MUL(a,b) a*b
#define DIV(a,b) a/bint main()
{int x,y;printf("x and y : ");scanf("%d%d",&x,&y);printf("x + y = %d\n",ADD(x,y));printf("x - y = %d\n",SUB(x,y));printf("x * y = %d\n",MUL(x,y));printf("x / y = %d\n",DIV(x,y));return 0;
}

宏的副作用

        由于宏是原样替换,在进行一些运算时可能会产生意外的结果。例如,对于 #define ADD(a,b) a+b ,如果使用 ADD(2, 3) * 4 ,会被替换为 2 + 3 * 4 ,而不是期望的 (2 + 3) * 4 。为避免这种情况,能加括号的地方都应加上括号,如 #define ADD(a,b) (a+b) 。


文件包含

文件包含是 C 语言预处理的一个重要操作,通过 #include 指令来实现。

#include <文件名> 和 #include "文件名" 是两种常见的文件包含形式,它们的主要区别在于查找头文件的方式不同。

  • 当使用 <文件名> 时,编译器会直接到系统默认的路径去寻找对应的头文件。这些系统默认路径通常由编译器的设置决定,一般包含标准库的头文件所在的位置。
    • 例如:#include <stdio.h> ,编译器会在系统默认路径中查找 stdio.h 头文件。
  • 当使用 "文件名" 时,编译器首先会在当前目录下寻找指定的头文件,如果在当前目录下没有找到,才会到系统默认路径下寻找。
    • 例如,如果当前目录下有一个自定义的头文件 myheader.h ,可以使用 #include "myheader.h" 来包含它。

在实际编程中,了解文件包含的查找方式有助于我们正确组织和管理项目中的头文件。

例如,如果我们正在开发一个较大的项目,并且有许多自定义的头文件,为了避免命名冲突和提高代码的可维护性,通常会将自定义头文件放在特定的目录中,并在使用时使用相对路径或绝对路径来指定头文件的位置。

另外,对于一些大型项目,可能会涉及多个开发人员同时工作,此时需要建立统一的文件组织结构和包含规则,以确保每个人都能正确地引用所需的头文件。

如果在包含头文件时出现找不到文件的错误,我们可以首先检查文件名是否正确,然后根据使用的包含方式,确认查找路径是否正确。

总之,合理使用文件包含,并清楚其查找机制,能够有效地提高代码的可重用性和可维护性。


条件编译

条件编译是 C 语言预处理的一个重要特性,它允许根据不同的条件来决定哪些代码被编译,哪些代码被忽略。

常见的条件编译形式

  1. #ifdef 标识符程序段 1
    #else程序段 2
    #endif
     

    含义:它的作用是若所指定的标识符已经被# define 命令定义过,则在程序编译阶段编译程序段 1; 否则编译程序段 2。其中# else 部分可以没有。

    举例:假设定义了标识符 DEBUG ,在调试时可以有以下代码:

    #ifdef DEBUGprintf("正在调试中...\n");#else// 正常运行时的代码#endif
  1. #ifndef 标识符程序段 1
    #else程序段 2
    #endif

    含义:只是第一行与第一种形式不同:将 "ifdef" 改为 "ifndef" 。它的作用是若标识符未被定义过则编译程序段 1; 否则编译程序段 2。这种形式与第一种形式的作用相反。

  2. #if 表达式程序段 1
    #else程序段 2
    #endif

    含义:它的作用是当指定的表达式值为真(非零)时就编译程序段 1; 否则编译程序段 2。可以事先给定条件,使程序在不同的条件下执行不同的功能。

    例如:#if 0 后面的程序段 1 会被当作注释处理。

用途

  1. 调试代码:在开发过程中,可以通过定义特定的标识符来开启或关闭调试输出,便于在不同的阶段控制代码的行为。

  2. 设计头文件:防止头文件被重复包含,通过条件编译来确保头文件中的内容只被处理一次。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#重要知识归纳总结

C#教程 C# 结构体&#xff08;Struct&#xff09; | 菜鸟教程C# 结构体&#xff08;Struct&#xff09; 在 C# 中&#xff0c;结构体&#xff08;struct&#xff09;是一种值类型&#xff08;value type&#xff09;&#xff0c;用于组织和存储相关数据。 在 C# 中&#xff0c…

微服务-实现nacos的集群和Gateway网关的实现、认证校验、解决跨域

1. nacos的集群模式 1.1 分析 nacos在企业中的使用100%都是集群模式。需要掌握nacos集群的搭建 nacos的数据存放在derby本地磁盘中&#xff0c;nacos集群模式会导致数据库数据不一致&#xff0c;使用加一层思想&#xff0c;修改nacos的数据库&#xff0c;使用mysql数据库&…

浅析中国蚁剑的木马加密流量

简介 在蓝帽杯 2022 初赛中&#xff0c;domainhacker 的流量分析题目聚焦于中国蚁剑这款 webshell 管理工具的流量特征。通过对比赛提供的数据包进行解析&#xff0c;本文将深入分析蚁剑在连接木马时产生的加密流量。 公司安全部门&#xff0c;在流量设备中发现了疑似黑客入侵的…

idea使用free流程,2024idea免费使用

1.先到官网下载&#xff0c;这里选择win系统的&#xff0c;点击下图的.exe https://www.jetbrains.com/idea/download/?sectionwindows 2.下载好后基本上就是一直点击“下一步”到直到安装好&#xff0c;安装好后先打开软件后关闭退出 3.下载配配套资料 链接: https://pan.ba…

MySQL内存模型与相关技术

MySQL实例的大概结构如下 如上图所示&#xff0c;InnoDB的存储引擎右多个内存块 维护所有进程/线程需要访问多个内部数据结构缓存磁盘上的数据&#xff0c;方便快速读取&#xff0c;且修改的数据缓存在此&#xff0c;满了后统一写入磁盘重做日志&#xff08;redo log&#xf…

5.3 需求分析

需求分析 软件需求定义分类练习题 需求工程需求获取练习题 需求分析状态转化图数据流图DFD顶层数据流图0层数据流图1层数据流图 练习题 需求规约需求定义方法 需求验证需求管理版本控制需求跟踪变更控制练习题 考试大概3分 软件需求 定义 软件需求&#xff1a;是指用户对目标…

【QT常用技术讲解】tableWidget右键菜单及多进程编程

前言 本文在QT项目的开发框架的基础上&#xff08;源代码&#xff09;增加tableWidget的右键菜单功能&#xff0c;并使用进程实现ping计算机的功能来讲解&#xff0c;本文不对进程间通信进行讲解。 概述 一个项目在开发过程中&#xff0c;通常面临着引入“第三方应用”&#x…

春秋云境 | 文件上传 | CVE-2022-30887

目录 靶标介绍 开启靶场 上传一句话木马 蚁剑连接 找到 flag 靶标介绍 多语言药房管理系统 (MPMS) 是用 PHP 和 MySQL 开发的, 该软件的主要目的是在药房和客户之间提供一套接口&#xff0c;客户是该软件的主要用户。该软件有助于为药房业务创建一个综合数据库&#xff0…

【代码随想录训练营第42期 Day22打卡 回溯Part1 - LeetCode 77. 组合 216.组合总和III 17.电话号码的字母组合

目录 一、做题心得 二、回溯基础知识 1.定义 2.适用问题 3.一个思想 4.代码实现 三、题目与题解 题目一&#xff1a;77. 组合 题目链接 题解&#xff1a;回溯 题目二&#xff1a;216.组合总和III 题目链接 题解&#xff1a;回溯 题目三&#xff1a;17.电话号码的字…

第十九天(2024.8.7)Vue Element-plus

1.Vue 1.创建vue文件 1.创建一个文件夹来存储vue文件 我在D盘下创建了一个EasyVue文件夹来存储vue文件 2.在控制台中输入 如果在控制台中按下面步骤成功不了的话&#xff0c;尝试&#xff1a;1.用管理员身份运行控制台 2.关闭防火墙 3.打开编码工具&#xff08;Visual St…

WPF学习(5)- Border控件(边框布局)+GridSplitter分割窗口

严格来说&#xff0c;Border并不是一个布局控件&#xff0c;因为它并不是Panel的子类&#xff0c;而是Decorator装饰器的子类&#xff0c;而Decorator继承于FrameworkElement。我们要先看看它的父类Decorator。 public class Decorator : FrameworkElement, IAddChild {public…

CUDA编程05 - GPU内存架构和数据局部性

一&#xff1a;概述 到目前为止&#xff0c;我们已经学会了如何编写 CUDA 核函数&#xff0c;以及如何设置和分配大量线程来执行核函数。我们还了解了当前 GPU 硬件的计算架构&#xff0c;以及线程在硬件上调度执行过程。在本章中&#xff0c;我们将重点关注 GPU 的片上(on-chi…

Redisson 实现分布式锁

文章目录 Redisson 是什么Redisson 使用客户端模式单节点模式哨兵模式主从模式集群模式Spring Boot 整合 Redisson 中的锁Redisson 可重入锁Redisson 公平锁Redisson 联锁Redisson 读写锁Redisson Redlock Redisson 的看门狗机制RedLock 解决单体故障问题如何使用 RedLockMarti…

【C语言篇】操作符详解(上篇)

文章目录 操作符详解&#xff08;上篇&#xff09;前言sizeof强制类型转换算术操作符赋值操作符逻辑操作符逻辑取反运算符逻辑与运算符逻辑或运算符 关系操作符自增自减操作符和-逗号表达式 操作符详解&#xff08;上篇&#xff09; 前言 操作符又被叫做运算符&#xff0c;是不…

进程状态(三)----- linux 中具体的进程状态(下)

目录 前言1. T && t 状态2. X 与 Z 状态3. 孤儿进程 前言 继上一篇文章 进程状态&#xff08;二&#xff09;----- linux 中具体的进程状态&#xff08;上&#xff09; 介绍了 linux 系统中具体的 R、S、D 状态&#xff0c;而这篇文章继续介绍 linux 系统中剩下的三种…

SpringBoot简单项目(二维码扫描)

pom.xml中导入依赖 <!-- zxing --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.0</version></dependency><dependency><groupId>com.google.zxing</gro…

探索七款前沿UI设计软件:创新与实践

之前我们分享了制作原型的有用工具。制作完原型后&#xff0c;我们需要优化界面&#xff0c;这就是 UI 设计师的任务了。UI 设计软件对设计师来说非常重要。UI 设计工具的使用是否直接影响到最终结果的质量&#xff0c;所以有人会问&#xff1a;UI 界面设计使用什么软件&#x…

Java批量查询CSDN质量分

文章目录 前言代码实现pom.xml实体类工具类质量分查询 效果开源仓库 前言 在CSDN平台申请“专家博主”、“优质创作者”等称号的时候&#xff0c;往往会对博客的质量分有一定的要求&#xff0c;这时候我们需要审视以往所发表的博客&#xff0c;找出质量分较低的博客&#xff0…

nordic 蓝牙ble 配对绑定的流程 原理

目录 配对和绑定的基本概念 配对和绑定的流程 1. 配对请求和响应 2. 配对方法选择 3. 密钥生成和交换 4. 配对完成和绑定 配对和绑定的代码实现 初始化Peer Manager 处理Peer Manager事件 处理BLE事件 启动广播 在Nordic芯片上实现蓝牙低功耗(BLE)设备的配对和绑定…

Python 为Excel单元格设置填充\背景色 (纯色、渐变、图案)

在使用Excel进行数据处理和分析时&#xff0c;对特定单元格进行背景颜色填充不仅能够提升工作表的视觉吸引力&#xff0c;还能帮助用户快速识别和区分不同类别的数据&#xff0c;增强数据的可读性和理解性。 本文将通过以下三个示例详细介绍如何使用Python在Excel中设置不同的单…