C语言 预处理详解(二) #命令行定义 #条件编译 #文件包含 #其他预处理指令

news2024/9/21 2:49:57

文章目录

前言

一、命令行定义

二、条件编译

三、文件包含

什么叫做文件包含?

(一)、本地文件包含

(二)、库文件包含

如何做才能避免头文件被多次包含呢?

方法一:利用条件编译:

方法二:利用 #pragma once

四、其他预处理指令

总结


前言

路漫漫其修远兮,吾将上下而求索;


一、命令行定义

许多C的编译器提供了一种能力,允许命令行中定义符号,用于启动编译的过程

例如,当我们根据同一个源文件要编译出一个程序的不同版本的时候,便可以利用到命令行;假设在一个数组中声明了某个长度的数组,如果机器的内存有限,此时就需要一个比较小的数组,但是倘若该机器的内存比较大,那么所需的数组就应该大一些;

注:在VS编译器下不好演示其效果,此处最好的在Linux 环境中的gcc 编译器来演示;

Linux 的命令行:

  • ls : list  -->  列出当前目录下的文件;
  • ls -l :将文件列举出来,像在ls 命令之后再跟一个 "-l" 这样的参数,此参数便叫作命令行参数;
  • gcc test.c -D  中命令行参数-D 可以定义一个符号

倘若代码中有些符号未定义,例如 SZ, 我们便可以在编译期间在命令行利用命令参数来指定该未定义符号的大小,如若你要利用命令行指令将SZ 赋值为100 ,便可以利用: gcc test.c -D SZ=100 

命令行定义在什么情况下用的比较多?

  • 我们写了一段代码,此代码会运行在不同的机器上,所以会针对不同的机器写出不同的代码,总是更改代码会很麻烦,于是乎便可以利用命令行来根据该机器针对性地给数据;

我们要如何才能写出一段代码而编译成不同的版本呢?

  • 在编译此版本的时候,将其参数部分进行配置即可(在编译的同时,为其参数指定一个值来配置);

此处利用命令行来确定数组的大小与变长数组有何区别?

  • 特别强调不可将此处利用命令行的指令来设置数组当作变长数组;此处的SZ这个符号的值是利用命令行定义的,而变长数组的大小是利用变量来指定的;

二、条件编译

什么叫做条件编译?

  • 在某些条件下的编译,即条件满足便去编译,条件不满足便不编译

倘若在编译一个程序的时候我们如果想要将一条语句(一组语句)编译或者放弃便就要利用到条件编译;

例:为数组赋值以将其值打印到屏幕的方式来观察此数组是否赋值成功:

但是,如果我们不想要打印数组元素的这个功能的话,可以将打印的这条代码删除,但是如果用来“调试”的代码很多,那么处理起来就很麻烦,因为你要将这些代码均找出来,况且也不敢保证下次就不会使用该代码,故而真的删除了也很可惜,于是乎此处便可以用到条件编译:

#ifdef symbol 

代码

#endif 

注:当symbol 为,即symbol 被#define 定义的时候,代码便会被执行;

当symbol 为假,即symbol 未被#define 定义的时候,代码便不会被执行;

使用如下图:

#ifdef __DEBUG__ --> 如果#define 定义过__DEBUG__ ,那么便会参与编译;反之,倘若#define 没有定义 __DEBUG__ ,那么 #ifdef __DEBUG__ 与 #endif 之间的代码便不会执行;

#endif 后面注释的 __DEBUG__  有什么用?

  • 可以明确地告诉我们与#ifdef __DEBUG__  是一对;因为在正常写代码的过程中,存在很多的条件编译,并且可以进行嵌套;倘若写的 #ifdef 多了,那么对应的 #endif 便也多了,谁和谁相匹配便难以分清;而此时倘若在对应的 #endif 上加上注释,我们便可显然得知其匹配的情况,增加了代码的可读性;

常见的条件编译指令:

1、条件编译

#if  常量表达式

            //……

#endif

//常量表达式由预处理器求值

2、多个分支的条件编译

#if  常量表达式

           //……

#elif

          //……

#else

         //……

#endif 

3、判断是否被否定

#if  defined(symbol)

        //……

#endif 

#ifdef symbol

        //……

#endif

#if  !defined(symbol)

       //……

#endif

#ifndef symbol

        //……

#endif

4、嵌套指令

#if defined(OS_UNIX)

                             #ifdef  OPTION1

                                        unix_version_option1();

                             #endif

                             #ifdef  OPTION2

                                        unix_version_option2();

                             #endif

#elif defined(OS_MSDOS)

                             #ifdef  OPTION3

                                        unix_version_option3();

                             #endif

#endif

具体使用如下:

例一:

使用

#if 常量表达式

            //……

#endif 

看到这,你可能会问是否可以利用当#if 的常量表达式为假的时候, 利用其来进行注释?

  • 不推荐,因为倘若使用这种方式来进行注释,当别人看到此代码的时候联想到的是条件编译,会让别人难以理解此代码的含义,降低了代码的可读性;

例二:

使用:

#if  常量表达式

              //……

#elif  常量表达式

              //……

#else  

              //……

#endif

具体使用如下:

 

我们看一下此程序预处理之后的结果,如下图:

例三:

使用:

#if defined(symbol)

#ifdef symbol

#if !defined(symbol)

#ifndef symbol

其具体使用如下:

注:条件编译在底层原码中用得很多,因为在底层原码中要处理跨平台、各种各样得细节问题,而针对各种各样得问题要进行适配;

三、文件包含

在前面得学习中我们得知,#include 指令使得编译程序将另一源文件嵌入到带有 #include 的源文件之中;在预处理阶段:先删除这条指令,然后用包含得文件的内容替换;

那么倘若一个源文件被包含了10次,那么实际中便会被编译10次;

什么叫做文件包含?

  • 在使用库函数时,我们通常要此库函数对应的头文件进行包含的操作;以及我们自己写的头文件也要在源文件中进行包含;

(一)、本地文件包含

#include "filename"

(二)、库文件包含

#include<filename.h>

注:头文件如果在源文件中被多次包含会使得我们的代码非常冗长

如何做才能避免头文件被多次包含呢?

即即使头文件被多次包含也不会极大地增加源代码的长度?

方法一:利用条件编译:

分析:

方法二:利用 #pragma once

想必仔细观察VS编译器的你会发现,当你创建一个头文件的时候,在此头文件中会自动包含 #pragma once ~

这种方法是一种比较现代的解决方法,在一些新的编译器下可用(在一些古老的编译器下不可使用此方法,eg. vc 6.0 不支持此法);

头文件的包含利用的是 < >,而当要包含自己的头文件的时候需用 " ", 这两种写法到底有什么区别呢?

  • < > 与 " " 的区别在于查找的策略不一样;
  • < > 的查找策略:直接在编译器所提供的库目录下去查找(或者在系统提供的库目录下去查找)
  • " " 的查找策略 : 先在自己本地所在的目录(该代码所在的路径下)下去查找,倘若找不到,便会去库目录下去查找;

故而其实也是可以用 " "  来包含库中的头文件,只不过这种方法比较慢而已,因为查了两个地方,而导致整体的效率比较慢;

日后包含头文件如何选择?到底是用 < > 还是 " " 呢?

  • 包含自己定义的头文件使用 " " , 而库目录中的头文件使用 < >  

四、其他预处理指令

预处理指令
预处理名称意义
#define宏定义
#undef撤销已经定义过的宏名
#include使得编译程序将另一源文件嵌入到带有 #include 的源文件之中
#if

#if 的一般含义是:如果 #if 后面的常量表达式为 true, 则编译它与 #endif 之间的代码

否则便会跳过这些代码;

命令#endif 标识一个 #if 块的结束;

#else 命令的功能有点像C语言中的else , #else 建立另一选择(在#if 的常量表达式为假的情

况下);

#elif 命令的意义与 else if 相同,它形成了一个 if else - if 阶梯状语句,可进行多种编

译选择

#else
#elif
#endif
#ifdef#ifdef#ifndef 命令分别表示”如果有定义“”如果无定义“ , 是条件编译的另一种方法 
#ifndef
#line

改变当前行数和文件名称,它们是在编译程序中预先定义的标识符命令的基本形式: 

#line number["filename"]

#error编译程序的时候,只要遇到 #error 就会生成一个编译错误提示信息,并停止编译
#pragma

可以设定编译程序完成一些特定的动作(可以通过编译程序的菜单设定,也可以直接写在

源代码之中),它允许向编译程序传递各种指令;例如,编译程序可能有一种选择,它支

持对程序执行的跟踪,可用 #pragma 语句指定一个跟踪选择


总结

1、命令行定义:许多C的编译器提供了一种能力,允许命令行中定义符号,用于启动编译的过程

2、条件编译:在某些条件下的编译,即条件满足便去编译,条件不满足便不编译

3、常见的条件编译指令:

1、条件编译

#if  常量表达式

            //……

#endif

//常量表达式由预处理器求值

2、多个分支的条件编译

#if  常量表达式

           //……

#elif

          //……

#else

         //……

#endif 

3、判断是否被否定

#if  defined(symbol)

        //……

#endif 

#ifdef symbol

        //……

#endif

#if  !defined(symbol)

       //……

#endif

#ifndef symbol

        //……

#endif

4、本地文件包含:#include "filename"

库文件包含:#include<filename.h>

5、即使头文件被多次包含也不会极大地增加源代码的长度?

方法一:利用条件编译 #ifndef #define #endif 

方法二: 利用 #pragma once

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

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

相关文章

鸿蒙界面开发——组件(8):图形绘制

绘制几何图形——父组件Shape 绘制组件的父组件&#xff0c;父组件中会描述所有绘制组件均支持的通用属性。 1、绘制组件使用Shape作为父组件&#xff0c;实现类似SVG的效果。 2、绘制组件单独使用&#xff0c;用于在页面上绘制指定的图形。 Shape(value?: PixelMap) value …

指针之旅(4)—— 指针与函数:函数指针、转移表、回调函数

目录 1. 函数名的理解 1.1 “函数名”和“&函数名”的含义 1.2 函数(名)的数据类型 2. 函数指针(变量) 2.1 函数指针(变量)的创建格式 2.2 函数指针(变量)的使用格式 2.3 例子 判别 3. typedef 关键字 3.1 typedef的作用 3.2 typedef的运作逻辑 和 函数指针类型…

全球瞩目丨2024深圳秋季糖酒会火热招商中

第 111 届深圳秋季全国糖酒会 2024 年 10 月29-31日 将在深圳国际会展中心&#xff08;宝安新馆&#xff09;盛大举行。 这是一场备受瞩目的行业盛会&#xff0c; 为企业提供了一个展示产品、 拓展市场、加强合作的绝佳机会。 作为亚洲地区食品行业规模最大、最具影响力的…

stm32之硬件SPI读写W25Q64存储器应用案例

系列文章目录 1. stm32之SPI通信协议 2. stm32之软件SPI读写W25Q64存储器应用案例 3. stm32之SPI通信外设 文章目录 系列文章目录前言一、电路接线图二、应用案例代码三、应用案例代码分析3.1 基本思路3.2 相关库函数介绍3.3 MySPI模块3.3.1 模块初始化3.3.2 SPI基本时序单元模…

指挥中心操作台厂家哪家好?选择时需要注意哪些?

在构建高效、稳定的指挥中心过程中&#xff0c;操作台作为核心设备之一&#xff0c;其选择至关重要。面对市场上琳琅满目的指挥中心操作台厂家&#xff0c;如何挑选出既符合需求又品质卓越的合作伙伴&#xff0c;成为众多采购者关注的焦点。接下来就给大家从以下几个方面探讨指…

uniapp设置隐藏qiun-data-charts数据标签

隐藏前&#xff1a; 隐藏后&#xff1a; 具体代码实现&#xff1a; 在opts配置中传入 "dataLabel": false 即可

个性化推荐兴趣社交社交平台

1 项目介绍 社交兴趣平台是一个基于 spring boot、vue3 的社交平台&#xff0c;旨在为用户提供一个分享、交流和发现各种有趣内容的场所。 该平台的核心功能是让用户能够创建个人主页并发布自己的动态、经历、见解和创意。用户可以自由发表各种主题的内容&#xff0c;涵盖但不…

《向量数据库指南》——非结构化数据大爆发,向量数据库引领电商推荐新潮流

在当今数据驱动的时代,数据作为企业的核心资产,其价值挖掘的深度与广度直接关乎到企业的竞争力和创新能力。长久以来,结构化数据因其规整的格式和易于分析的特性,成为了数据科学家和工程师们研究的热点,其潜力在多个领域已被充分挖掘和应用。然而,随着互联网的飞速发展,…

C语言常见运算符

C语言提供了丰富的运算符&#xff0c;这些运算符用于执行各种类型的操作&#xff0c;比如算术运算、比较运算、逻辑运算、位运算等。下面是一些基本的C语言运算符分类及其示例&#xff1a; 1. 算术运算符 加法 (): a b 表示a和b的和。减法 (-): a - b 表示a和b的差。乘法 (*…

2024年Ai智能绘画Stable Diffusion软件+整合包+保姆式教程

前言 在2024年的科技浪潮中&#xff0c;一款名为Stable Diffusion的AI智能绘画软件吸引了全球的目光。它不仅为艺术家和设计师提供了无限创意的可能&#xff0c;也让我们每个人都能轻松体验绘画的乐趣。那么&#xff0c;Stable Diffusion究竟有何魅力&#xff1f;它又是如何工…

消息队列实现多人聊天

消息队列实现多人聊天 分析&#xff1a; 每个程序都有两个任务&#xff0c;一个任务是负责接收消息&#xff0c;一个任务是负责发送消息&#xff0c;通过 fork 创建子进程实现多任务。 一个进程负责接收信息&#xff0c;它只接收某种类型的消息&#xff0c;只要别的进程发送…

2024i春秋第四届长城杯网络安全大赛暨京津冀网络安全技能竞赛初赛wp-flowershop+easyre

flowershop 如图所示&#xff0c;v8是钱的数量&#xff0c;strncpy从src复制三个字符给dest的数组里&#xff0c;最后一个元素是0&#xff0c;相当于字符串截断&#xff0c;strcmp是比较c和dest中的内容&#xff0c;如果相等就不会exit(0)&#xff1b;双击c进去看&#xff0c;c…

Pr:新建序列 - 设置

在“新建序列”对话框中&#xff0c;“设置” Settings选项卡的主要用途是选择预设或手动配置序列的参数&#xff0c;以确保与用户的素材和输出需求相匹配。 可以根据项目的具体要求自定义序列中的视频和音频格式&#xff0c;包括帧速率、分辨率、工作色彩空间、音频采样率等以…

nvm install 16.14.1报错“Node.js v16.14.1 is not yet released or available.”

原因&#xff1a; 使用命令nvm ls available&#xff0c;结果列出的可供下载的版本列表为空&#xff0c;这就是原因了 解决办法&#xff1a;修改镜像 nvm node_mirror https://npmmirror.com/mirrors/node/ nvm npm_mirror https://npmmirror.com/mirrors/npm/ 结果&#xf…

基于SpringBoot+Vue+MySQL的校园生活服务平台

系统展示 用户前台界面 管理员后台界面 系统背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可跨域的鸿沟&#xff0c;信息的…

springboot优雅停机无法关闭进程,kill无法停止springboot必须kill -9,springboot线程池使用

背景最近项目在jenkins部署的时候发现部署很慢&#xff0c;查看部署日志发现kill命令执行后应用pid还存在&#xff0c;导致必须在60秒等待期后kill -9杀死springboot进程 应用环境 springboot <dependency><groupId>org.springframework.boot</groupId>&l…

u盘怎么制作win10启动盘_win10启动盘制作详细教程

u盘怎么制作win10启动盘&#xff1f;制作win10启动盘方法有很多&#xff0c;有官方制作方法&#xff0c;有第三方u盘启动盘制作方法&#xff0c;下面小编就教大家制作win10启动盘详细教程。 u盘怎么制作win10启动盘&#xff1f; 制作win10启动通常有两种方法&#xff1a;直接安…

带有HSE组件的S32系列芯片中各子系统如何依次启动?

《S32系列芯片——Boot详解》系列——带有HSE组件的S32系列芯片中各子系统如何依次启动&#xff1f; 一、各子系统的重置释放顺序二、启动流程2.1 安装启动过程2.2 正常启动流程 博主已开通同名公众号&#xff0c;通过文末或主页二维码关注博主&#xff0c;将为你推送最新、最细…

音乐网站-前后台登录注册搜索试听下载评论音乐分计算机毕业设计/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

1. 前台功能模块 首页&#xff1a; 展示热门音乐、推荐音乐、最新发布。搜索框&#xff1a;支持音乐、专辑、艺人等的搜索。用户登录/注册入口。 用户注册和登录&#xff1a; 用户注册&#xff1a;输入用户名、密码、邮箱等信息。用户登录&#xff1a;输入用户名和密码。密码找…