include 嵌套 重复 文件包含使用条件编译处理

知识点滴 - 关于头文件的重复包含问题 对于函数声明来说,如果一个编译单元里出现多次相同的声明没有问题,但如果是一个编译单元里对同一个对象(变量)或类型有多个定义,或者是同一个作用域里有重复定义,比如全局空间有重复定义,就会编译报错。重复包含一个头文件,可能会导致编译错误,就算头文件中都是可以重复出现的声明之类的内容,多次包含的话,也会增加预处理器或编译器的工作量,增加软件构建的时间。在一个源文件中包含此头文件my_include.h时,会检查此宏定义是否定义,如果是第一次包含,没有定义,则下一步定义这个宏,并将后面的内容包含进来。 阅读详情

          编译一个C程序涉及很多步骤。其中第1步被称为预处理。C预处理器在源代码编译之前进行一些文本性质的操作。它的主要任务包括删除注释、插入被#include指令包含的文件内容、定义和替换由#define指令定义的符号等。

          C和C++中都使用#include 指令包含头文件。#include 支持包含函数库文件和本地文件。

          我们在写一个小程序的时候,大多数情况下不会遇到这种嵌套包含的问题。但有些同学尝试使用多个头文件时,常会遇到“与‘**’类型冲突”的错误,最后不得将多个头文件合成一个。有些同学还会奇怪的问,为什么函数库头文件可以多次包含,而我的就不行呢?

让我们假设下面这种情况是一种比较简单的嵌套头文件包含情况:

a.h文件有一个结构体声明:mystruct

b.h 使用#include “a.h" 包含了a.h ,同时定义了一个函数print_b()

c.h 使用#include “a.h" 包含了a.h ,同时定义了一个函数print_c()

d.c 同时包含了 b.h 和c.h

关系如下图:


a.h


#include<stdlib.h>
#include<stdio.h>
#include<string.h>
typedef struct mystruct{
    char a[10];
}mystruct;

b.h

#include "a.h"
void print_b(){
    mystruct a;
    strcpy(a.a,"a.h");
    printf("this is in b.h %s\n",a.a);
}

c.h

#include "a.h"
void print_c(){
    mystruct c;
    strcpy(c.a,"a.h");
    printf("this is in c.h %s\n",c.a);
}

d.c

#include "b.h"
#include "c.h"
int main(){
    printf("this is in main\n");
    print_b();
    print_c();
    return 0;
}

gcc a.h b.h c.h d.c -o d.o 不能编译成功,有错误

# gcc a.h b.h c.h d.c -o d.o
In file included from c.h:1:0,
                 from d.c:2:
a.h:4:16: 错误: ‘struct mystruct’重定义
a.h:4:16: 附注: 原先在这里定义
a.h:6:2: 错误: 与‘mystruct’类型冲突
a.h:6:2: 附注: ‘mystruct’的上一个声明在此

原因是,预处理的时候,会读入整个头文件的内容,a.h的内容被重复包含了两次。出现了类型冲突。

在编译一个程序的时候,使用条件编译,你可以选择代码的一部分(或全部)是被正常编译还是完全忽略。这里,它就可以很好的派上用场。

替换a.h为下面内容

a.h

/*a.h*/
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#ifndef _a_H
#define _a_H 1
typedef struct mystruct{
    char a[10];
}mystruct;
#endif


gcc a.h b.h c.h d.c -o d.o

./d.o

this is in main
this is in b.h a.h
this is in c.h a.h


发现并没有类型冲突。

其实就是在a.h中使用了条件编译(这也是函数库头文件可以多次包含的原因)。

#ifndef _a_H 

#define _a_H 1

/*这是是你头文件本来的内容*/

#endif

这样多次重复包含的危险就被消除了。当头文件第一次被包含的时候,它正常处理,符号 _a_H( 可以自己定义符号 )被定义为 1(这个不是必须的,也可以直接写成#define _a_H)。如果头文件再次被包含时,通过条件编译,它说包含(#Iinfdef ...#endif 之间)的内容就会被忽略。


这里的示例只是为了更清楚的看到条件编译的作用,所以只在a.h当中使用了,其实如果我们所有头文件都是要上述的条件编译的方法编写,就可以消除多次包含的危险。因为其他人使用你的头文件的时候,可能并不清楚它已经包含了多少其他文件。(最好能够尽避免多次包含,因为预处理仍会读入整个头文件,即使它已经被忽略)


当然条件编译的作用不仅仅于此,比如在编写程序的时候需要debug的时候要经常用到。一些debug在编写程序阶段需要,当交付是有不需要让他们输出,这时候条件编译就大有用处。(下面是我常用的,注意这是使用的是#ifdef)

/*#define DEBUG_MIGRATION
*/
#ifdef DEBUG_MIGRATION
#ifdef CONFIG_LINUX
#include <sys/syscall.h>
#define DPRINTF(fmt, ...)                                               \
    do {                                                                \
        printf("%d:%ld %s:%d: " fmt, getpid(), syscall(SYS_gettid),     \
               __func__, __LINE__, ## __VA_ARGS__);                     \
    } while (0)
#else
#define DPRINTF(fmt, ...)                                               \
    do {                                                                \
        printf("%s:%d: " fmt, __func__, __LINE__, ## __VA_ARGS__);      \
    } while (0)
#endif
#else
#define DPRINTF(fmt, ...)       do { } while (0)
#endif


需要输出debug的时候就#define DEBUG_MIGRATION ,不需要的时候就注释它,那么整个文件的debug信息就不会输出。

下面是使用的范例:

DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
需要输出debug的地方就使用DPRINTF。

常用的语法形式如下:

#if constant-expression
      statements
#elseif constant-expression
      statements2
#else
      other statements
#endif

常用的判断是否被定义的有

#ifndef   something/* something 没有被定义*/

#ifdef     something/*something 已经被定义*/


参考:

pointers on c, Kenneth A.Reek

QEMU/KVM源码


caffe深度学习【十】编译错误:In file included from ./include/caffe/util/device_alternate.hpp:40:0, 早上想跑一下目标检测RON算法 https://github.com/taokong/RON   git下来之后,要编译他的caffe时,出现如下问题: PROTOC src/caffe/proto/caffe.proto CXX .build_release/src/caffe/proto/caffe.pb.cc CXX src/caffe/solvers/adadelta_solve... 阅读详情

相关推荐

文件包含漏洞(DVWA靶场文件包含漏洞详解)

浏览器中运行此日志文件,即可执行插入的PHP代码。注意:若是一句话代码,就可以控制此服务器了。查看日志文件已记录此条信息。1、文件包含漏洞形成原因。2、文件包含漏洞的分类。3、PHP包含日志文件。

tmzy1的博客 2532

头文件互相包含解决_【C语言】- 预处理指令3 - 文件包含

这篇小文介绍最后一个预处理指令---文件包含一、基本概念其实我们早就有接触文件包含这个指令了, 就是#include,它可以将一个文件的全部内容拷贝另一个文件中。二、一般形式1、第1种形式#include <文件名>直接到C语言库函数头文件所在的目录中寻找文件2、第2种形式 #include "文件名"系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到...

weixin_39950470的博客 976

C/C++ 头文件防卫:告别重复定义错误,掌握 #include Guards 与 #pragma once

在C/C++项目开发中,头文件重复包含是一个常见问题,可能导致编译错误。重复包含通常发生在直接或间接多次包含同一个头文件时,尤其是当头文件中包含变量、结构体或枚举定义时。为了解决这一问题,C/C++提供了两种主要方法:Include Guards和#pragma once。Include Guards通过宏定义和条件编译指令确保头文件内容只被处理一次,而#pragma once则是一个非标准但广泛支持的指令,直接告诉编译器头文件只需包含一次。选择哪种方法取决于项目需求和编译器支持,Include Guard

Web安全工具库 1690

避免同一个文件被include多次

百度问题:(转自:http://zhidao.baidu.com/question/112685790.html?si=1)我们知道c++有时会遇到防止头文件重复include问题,常用的方式可以防止.h文件重复include 的方式为:#if !defined...#define... 但是在有些地方发现了这段代码#if _MSC_VER > 1000#pragma once#e

bdewang的专栏 1207

SAP ABAP开发避坑:BAPI_MATVAL_PRICE_CHANGE调用报‘估价未维护’的完整解决流程

本文深入解析SAP ABAP开发中调用BAPI_MATVAL_PRICE_CHANGE函数时遇到的'估价未维护'错误,提供从基础配置检查到CKML_PRICE_SEND表维护的完整解决方案。通过实战案例和代码示例,帮助开发者快速定位问题并实现批量价格更新的自动化处理,提升开发效率。

weixin_28705087的博客 427

STM32+ESP8266接入OneNet的MQTT嵌入式实现

MQTT是一种轻量级发布/订阅模式的消息传输协议,广泛应用于资源受限的物联网终端设备通信。其基于TCP/IP栈构建,具备低带宽占用、小代码体积和高可靠性等技术优势,特别适合STM32等MCU与Wi-Fi模组协同工作的边缘节点场景。在实际工程中,需结合AT指令集控制ESP8266完成Wi-Fi连接、TCP建链及MQTT会话管理,并通过环形缓冲区、空闲中断检测等机制保障UART异步通信稳定性。本文围绕STM32F103与ESP8266硬件协同、OneNet平台对接、JSON消息解析与OLED状态反馈等关键环节,

yhn456789的博客 1037

漫谈:C C++ 嵌套包含与前置声明

漫谈:C C++ 嵌套包含与前置声明

编程之道在明明德在亲民在止于至善 1562

逆向小记 bd-ticket-guard-client-cert | bd-ticket-guard-client-data 通过现象看本质

依然没什么花哨的直接解析出来,既然这是个X509证书,那么要么是服务器下发,要么是自己生成喽,但是如果是本地直接生成,那服务器还校验个鬼哦!既然是服务器下发的,那服务器又是怎么校验这个证书是自己下发的呢,这就要讲到CA验证了,首先根据上图我们发现证书的Issuer"(颁发者)是。怎么来的呢,因为涉及到逆向咱就不讲了,其实也是涉及到非对称加密的,就是客户端用私钥去签名数据,服务器用上传过去的证书公钥验签的过程!当服务器收到这个证书的时候,会做几件事,首先用自己的公钥解开签名获得解密后的证书hash。

2503_90751789的博客 1205

C/C++基础 -- 预处理命令(宏定义、文件包含、条件编译)

C/C++基础 -- 预处理命令1、预处理命令2、宏定义2.1、不带参数的宏定义2.2、带参数的宏定义3、# 和 ## 预处理运算3.1、# 预处理运算符3.2、## 预处理运算符4、文件包含 本博文由 西北工业大学MOOC 总结而来,以备以后回顾。(侵删) 1、预处理命令 什么是预处理命令   在C++文件中,只要是以 “#” 开头的命令都称为预处理命令。   预处理命令不是C++本身的组成...

不世峰兄 的博客 2929

杰理SDK入门教程(六):自定义按键事件

这篇教程介绍了在杰理AC695N芯片上实现自定义按键功能的完整流程,包含三个核心步骤:首先在key_event_deal.h文件中添加按键枚举定义;然后在adkey_table.c配置按键映射表,按模式和行为类型设置按键值;最后在app_common.c的消息处理函数中添加自定义按键逻辑。教程强调需注意不同模式按键表的列定义差异,并提供了具体代码示例,最后指导完成编译测试验证。全文结构清晰,配有关键代码截图,适合开发者快速上手自定义按键功能开发。

qq_42014563的博客 1017

[密码学实战]商密TLCP协议抓包解析与深度分析(二十九)

[密码学实战]商密TLCP协议抓包解析与深度分析(二十九)

曼岛_的博客 632

(二十三) 23.2 文件包含和条件编译

(二十三) 23.2 文件包含和条件编译

lcc1737的博客 257

【Agent】AI Agent架构趋势及演进

本文系统介绍了AI Agent架构的发展趋势和技术框架,涵盖单Agent与多Agent系统设计、提示词工程与上下文管理、SpringAI开发框架等关键技术。重点阐述了AI原生应用参考架构,包括Nacos配置管理、Higress AI网关、RocketMQ会话状态管理等核心组件解决方案。同时深入探讨了AI应用可观测性体系,包含OpenTelemetry全链路追踪、关键指标监控以及基于LoongSuite开源工具的数据采集评估方法,为构建高效可靠的AI应用提供了全景式技术参考。

一个内容严谨、方向前沿、极具实践价值的AI大模型技术分享站 1106

C++报错无效的预处理命令include_C++日志(十七)编译预处理

1、#include命令文件包含所使用的“#include”命令,目的是将另一个源文件嵌入到本源文件中,其形式包含下面两种:使用#include<文件名>形式,在系统目录下进行搜索;使用#include"文件名"形式,先在当前目录进行搜索,搜索不到转到系统目录下搜索;最后,#include指令支持嵌套使用,即xxx.h文件中仍可以使用#include指令包含其它的.h文件。2、#def...

weixin_39782832的博客 1363

1️⃣ Coze智能体基础入门教学(2025年全新版本)

不妨设想一下,倘若我们能够打造出一个数字助手,它不但能精准洞悉我们的需求,更可以独立思索、学习以及付诸行动,这便是 AI Agent 的基础理念。它恰似计算机世界里的 “智慧精灵”,能够感知周遭环境、做出判断并采取行动。AI Agent 的发展之路精彩纷呈。它走过了三个重要阶段:起初,它犹如一个被规则束缚的机器人,只能按预设好的指令开展工作。接着,它学会运用统计学方法,通过分析数据来做决策,变得越发灵活。到如今,凭借深度学习技术,它已经能够像人类一样去学习复杂的任务了。

李同学Lino的博客 4086

EZ-INSAR工具箱(使用历史问题)

把 rasterio 升到 1.3+(或退回 py39)→features能 import → 脚本能跑 → 出图 → MATLAB 不再报错!

2301_79258000的博客 121

c语言include不起作用,c语言中include的使用方法

c语言中include的使用方法发布时间:2020-06-16 09:09:37来源:亿速云阅读:185作者:Leah这篇文章将为大家详细讲解有关c语言中include的使用方法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1、#include 命令介绍#include 命令是预处理命令的一种,预处理命令可以将别的源代码内容插入到所指定的位置;可以标识出只有在特...

weixin_33605464的博客 2038

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【C语言】17-预处理指令3-文件包含

一、基本概念 其实我们早就有接触文件包含这个指令了, 就是#include,它可以将一个文件的全部内容拷贝另一个文件中。   回到顶部 二、一般形式 1.第1种形式#include  直接到C语言库函数头文件所在的目录中寻找文件   2.第2种形式 #include "文件名" 系统会先在源程序当前目录下寻找,若找不到,再到操作系统的pat

u013948187的专栏 393

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

include、require、include_once、require_once的区别

include、require、include_once、require_once的区别

资深系统架构,深耕高并发研发与 AI 大模型工程化落地。技术栈以 Python (AI/NLP/RAG/Agent) 与 Golang (高并发架构) 为双核心,兼具 PHP 大型应用经验。专注算法落地、复杂系统设计及团队技术赋能 2058

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

C++预处理中的“文件包含”功能(#include)

所谓“文件包含”,是指一个文件可以将另外一个文件的全部内容包含进来,也就是将另外的文件包含到本文件中。C++语言中,通过 #include 命令来实现。其一般形式如下: 虽然可以用#include把任何一个其他文件的内容包含到当前文件中,但是在C++语言中,最常见的做法还是一些源程序文件(扩展名为.c或者.cpp等)用#include把一些头文件(扩展名为.h或者.hpp等)包含进来,这种包含的感觉如图1所示。 图 1 用 #include 将 head.h 内容包含到 a.cp

m0_51073072的博客 570

PHP学习之文件包含

PHP学习之文件包含 定义与产生 在项目实践中,有很多的代码需要重复调用,这是只要将需要重复调用的代码写在一个文件当中,重复调用这个文件就好。 分类 动态、静态:输入内容用户是否可控 远程、本地:文件源在哪里 向上、向下:被包含文件是作为文件源,还是输出通道 主要函数 include :包含并执行文件 include_once 与include相同,区别在于会自动判断是否某个文件,一个文件只包含一次 require require_once require 、require_once 与in

weixin_45960266的博客 296

ABAP结构体嵌套实战:INCLUDE TYPE和INCLUDE STRUCTURE的5个典型应用场景

本文深入探讨了ABAP中结构体嵌套技术INCLUDE TYPE和INCLUDE STRUCTURE的5个典型应用场景,包括基础数据结构复用、多源数据结构合并、带前缀/后缀的字段复用、动态结构扩展和类型安全的嵌套结构。通过实际代码示例展示了如何高效处理复杂数据结构,提升代码复用性和可维护性,是ABAP开发者必备的核心技能。

weixin_30485379的博客 443

Daily-C-Study(17):C语言文件包含#include

Daily-C-Study(17):C语言文件包含#include 成于坚持,败于止步 文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。C语言提供#include 命令来实现文件包含的操作,它实际是宏替换的延伸,有两种格式: 一、#include 其中,filename 为要包含的文件名称,用尖括号括起来,也称为头文件,表示预处理到系统规定的...

Ryze的博客 501

【C语言】15 预处理指令--文件包含

1.基本概念 其实我们早就有接触文件包含这个指令了,就是#include,它可以将一个文件的全部内容拷贝另一个文件中。   2.一般形式 1.第1种形式#include  直接到C语言库函数头文件所在的目录中寻找文件   2.第2种形式 #include "文件名" 系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找   3.使用注

LiJin 653

php基础-文件包含(第13天)

摘要:PHP文件包含功能允许在一个脚本中引入另一个文件,实现代码复用和分工协作。主要包含四种形式:include/include_once和require/require_once,区别在于错误处理方式及是否重复包含。文件加载时会被嵌入到包含位置,并单独编译。路径分为绝对路径和相对路径,各有优缺点;嵌套包含时建议使用__DIR__或绝对路径避免相对路径错误。核心原则:必需文件用require,可选文件用include,优先使用_once版本确保安全。

weixin_48813932的博客 709

PHP 文件包含、文件加载、文件路径、文件嵌套包含

1. 2.在PHP中被包含的文件是单独进行编译的 3. 4.include和require都是文件包含,区别在于报错不一样。include获取不到文件,可以继续执行下面的语句,而require不行。 5.include可以执行多次,而include_once只允许执行一次 6....

liuyi0604的博客 959
上一篇: 1053. Path of Equal Weight (30)-PAT
下一篇: 1052. Linked List Sorting (25)-PAT
zoushidexing
博客等级 码龄18年 70粉丝 171原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值