C/C++ 中头文件相互包含引发的问题

2023-05-16

今天下午遇到一个头文件相互包含而导致的编译问题,花了我不少时间去调试没找到问题,最后晚上跟师兄讨论不少时间,突然有所顿悟!

问题重现

我把问题脱离于项目简单描述一下:我写了一个函数 bool func(ClassA* CA) 需要加到项目中,我就把这个函数的声明放到 head1.h 中,函数参数类型 ClassA 定义在另一个头文件 head2.h 中,因此我需要在 head1.h 中包含 head2.h;而 head2.h 中之前又包含了 head1.h,这样就构成了一种头文件相互包含的场景。再加上一些其它的声明与定义,就构成了这样的一个文件结构:

  • head1.h

#ifndef __HEAD_1_H__
#define __HEAD_1_H__

#include "head2.h"

#define VAR_MACRO  1          //define a macro, which used in head2.h

bool func(ClassA* CA);        //ClassA is defined in head2.h

#endif 
  
  • head2.h

#ifndef __HEAD_2_H__
#define __HEAD_2_H__

#include "head1.h"

class ClassA{
  int mVar;
  void setMem(){ mVar = VAR_MACRO };    //macro VAR_MACRO is defined in head1.h

  ...     //other members and functions
};

#endif 
  

那么,现在另有两个源文件

  • source1.cpp

#include "head1.h"

//... some source code

  
  • source2.cpp

#include "head2.h"

//... some source code

  

整个项目会分别编译这两个源文件,编译完之后会报错,大致意思是 ClassA 和 VAR_MACRO 没有定义,那么问题就比较奇怪了,每个头文件都分别引用了另一个头文件,为什么会出现未定义呢?

问题分析

我们都知道 C/C++ 中头文件开始习惯使用 #ifndef ... #define ... #endif 这样的一组预处理标识符来防止重复包含,例如上面的问题中我也使用了,如果不使用的话,两个头文件相互包含,就出现递归包含。这个很好理解就不多叙。

回到问题本身,我在微博上贴出了这个问题,有人说在 head1.h 的函数前加上 ClassA A; 的前置声明,至于为什么要加,估计很多人都没理解...

对于 source1.cpp,它包含了头文件 head1.h,那么在编译之前,在 source1.cpp 中展开 head1.h,而 head1.h 又包含了 head2.h, 那么也展开它,这时 source1.cpp 就变成类似下面这样:


class ClassA{
  int mVar;
  void setMem(){ mVar = VAR_MACRO };    //macro VAR_MACRO is defined in head1.h

  ...     //other members and functions
};

#define VAR_MACRO  1          //define a macro, which used in head2.h

bool func(ClassA* CA);        //ClassA is defined in head2.h

//... source1.cpp source code
  

看到没,这地方 func 函数之前有 ClassA 类型的定义,根本没必要像有些人说的那样加上 ClassA CA; 这样的前置声明。

我们再展开 source2.cpp 看看:


#define VAR_MACRO  1          //define a macro, which used in head2.h

bool func(ClassA* CA);        //ClassA is defined in head2.h

class ClassA{
  int mVar;
  void setMem(){ mVar = VAR_MACRO };    //macro VAR_MACRO is defined in head1.h

  ...     //other members and functions
};

//... source2.cpp source code
  

这时问题就很清楚了,func 函数声明之前并没有发现 ClassA 类型定义,该定义在函数声明的后面,这时候如果能在head1.h 的函数声明之前加上 ClassA CA; 的前置声明,就不会在编译的时候报找不到 ClassA 的定义的错误了。

再回到 source1.cpp 展开的源码看看,是不是一下子明白了为什么报找不到 VAR_MACRO 的定义的错误了?修改方法也简单,把宏定义拉到 #include "head2.h" 语句之前就 OK 了。

问题反思

现在回头想想这个问题,其实是个非常简单的头文件包含的问题,如果了解一些编译器的预编译过程,错误原理也很简单。但为什么我卡在这个问题很长时间,原因有以下几点:

  • 问题出现在一个项目的编译过程中,未能准确地定位问题的原因
  • 出现问题,没有静下来心来理清问题,C/C++ 编译基础知识点虽然知道,但未能第一时间运用到该问题的分析上
  • 师兄说加上前置类的声明,确实解决了类类型找不到的错误,虽然有问题解决方法,但没有真正理解这种做法的道理,以至于宏定义找不到的错误还是不知如何去解决(其实本质是一样的)

总的来说,遇到问题不要慌,保持大脑清醒,把加一行或减一行代码期望就能碰运气编译通过的时间拿来分析问题更有效,解决问题之后一定确定自己是否知其然亦知其所以然!

参考资料

Google找了一圈,没找到有价值的资料....

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++ 中头文件相互包含引发的问题 的相关文章

  • mongodb创建用户名和密码(版本5.0.6)

    mongodb创建用户名和密码 xff08 版本5 0 6 xff09 官方文当地址 xff1a 1 mongodb 添加用户 2 mongodo将命令行参数转化为yaml参数的方法 将命令行选项转换为YAML 超级有用哦 3 mongod
  • Lua字符串切分

    lua 字符串切分示例 local requrl 61 34 10 20 144 127 8891 cas login 34 local cutstr 61 string sub requrl 2 print cutstr local cu
  • nginx lua开发配置文件详解

    加载lua文件和库文件 lua package path 34 waf nginx cnwaf lua waf nginx init lua waf nginx cnwaf plugins lua waf nginx cnwaf deps
  • win7系统安装使用高版本的nodejs-v16.19.1

    文章目录 参考 参考 win7这样操作 xff0c 可成功安装高版本node js xff01 注意 xff1a 首先安装13 14版本nodejs 删掉安装目录下的内容下载16 19 1版本的nodejs版本 xff08 下载zip的包
  • j记录一次gorm 使用协程 插入数据一直报错问题 -Duplicate entry ‘95‘ for key ‘PRIMARY‘ [14.649ms] [rows

    文章目录 问题描述问题解决 问题描述 报错代码如下 span class token keyword func span span class token function GetSystemInfoSave span span class
  • stable diffusion的使用

    文章目录 1 文生图1 1 mountains and trees and gree1 2 three dogs1 3 cats1 4 three lovely cats1 5 beautiful girl1 6 机器猫1 7 卡通图像生成
  • DIY:用开源软件搭建自己的物联网

    原文链接 xff1a DIY Open Source Software for your very own IoT 作者 xff1a Vishal Shah 翻译 xff1a 赵屹华 审校 xff1a 刘翔宇 图片来源 xff1a open
  • ubuntu开启rdp服务

    概要 ssh登录用于终端 xff0c 如果需要GUI的远程登陆ubuntu 我了解到大概3中方案 vncxrdp第三方软件 向日葵 TeamViewer之类的 因为vnc我一直配置不好 xff0c 所以试了一下xrdp xff0c 这样wi
  • csp模拟2-T1 HRZ的序列

    题目 时间限制 1s 空间限制 64MB 题目描述 相较于咕咕东 xff0c 瑞神是个起早贪黑的好孩子 xff0c 今天早上瑞神起得很早 xff0c 刷B站时看到了一个序列aaa xff0c 他对这个序列产生了浓厚的兴趣 他好奇是否存在一个
  • Ubuntu 中文件和目录的操作命令

    在 Ubuntu 中 xff0c 文件和目录的操作命令是非常重要的 这些命令帮助您在文件系统中创建 复制 移动 删除和查看文件和目录 以下是一些常用的文件和目录操作命令 xff1a cd cd 命令用于切换当前工作目录 例如 xff0c 要
  • Docker无法在WSL2的Ubuntu启动的问题

    今天在更新了WSL2上的Ubuntu22 04版本 xff0c 在安装Docker后无法启动 xff0c 查看Docker的日志显示如下的错误 INFO 2022 04 22T16 14 55 718999500 43 08 00 stop
  • C语言中的移位操作

    C语言中的移位操作 xff0c 内容不多 不过有些地方你不注意 xff0c 就疏忽了 先做两个小题先 1 unsigned char x 61 3 x lt lt 1是多少 xff1f x gt gt 1是多少 xff1f 2 char x
  • Android获取设备唯一标识的方法

    String uniqueId String mac 61 getMacAddressByInetAddress if mac 61 null amp amp mac equals 34 34 amp amp mac equals 34 0
  • Linux 搭建私有CA证书服务器之超详细版本

    一 CA简介 CA是什么 xff1f CA是Certificate Authority的简写 xff0c 从字面意思翻译过来是凭证管理中心 xff0c 认证授权 它有点类似我们生活中的身份证颁发机构 xff0c 这里的CA就相当于生活中颁发
  • 基于Nginx搭建RTMP-HLS视频直播服务器(推流+拉流)

    1 环境准备 Linux centos7 6 nginx 1 18 0 源码包 span class token function wget span http nginx org download nginx 1 8 1 tar gz n
  • k8s-部署本地仓库harbor

    1 基础配置 xff1a 主机名IP系统版本k8s master192 168 32 128centos 7 6k8s node1192 168 32 129centos 7 6k8s node2192 168 32 130centos 7
  • k8s部署nginx容器

    1 创建挂载nginx namespace yaml配置文件 xff08 k8s master xff1a 192 168 32 128 xff09 apiVersion v1 kind Namespace metadata name ng
  • k8s部署tomcat并且映射本地目录

    1 编写Dockerfile span class token punctuation span root 64 VM 12 7 centos opt span class token punctuation span span class
  • 自动化运维记录之GitLab CI/CD 自动化部署入门教程

    1 前端项目自动化部署需要的环境依赖 Node 安装项目依赖 打包都需要 Nginx web 项目部署必须 正向代理 方向代理 负载均衡等等 GitLab 也会用到 Nginx span class token punctuation sp

随机推荐

  • k8s-kubeadm证书过期续订解决方法

    1 实验目的 通过kubeadm安装的kubernetes集群各个组件所使用证书的期限为1年 xff0c 本实验练习的是到期之后如何续期 2 实验环境 ubernetes环境及版本 整个实验三台机器192 168 32 128作为maste
  • 宝塔部署Django项目-避坑必看

    1 在linux windoes机器上已经安装宝塔 浏览器登录宝塔管理页面 1 1打包模块 span class token number 1 span 将本地计算机的项目下的模块打包 python m pip freeze span cl
  • week11作业—A - 必做题11-1—

    题目 蒜头君从现在开始工作 xff0c 年薪 NNN 万 他希望在蒜厂附近买一套 606060 平米的房子 xff0c 现在价格是 200200200 万 假设房子价格以每年百分之 KKK 增长 xff0c 并且蒜头君未来年薪不变 xff0
  • Linux 系统 nginx 源码编译安装

    nginx版本 xff1a nginx 1 18 0 操作实施环境 Ubuntu 16 04 6 LTS SUSE Linux Enterprise Server 12 SP4 x86 64 注 xff1a 因为是源码安装 xff0c 操作
  • ubuntu18.04和20.04(ubuntu focal)安装MySQL8并使用navicat连接(详细)

    文章的第一节转载自原文连接 本文实现了服务器ubuntu18 04和虚拟机ubuntu20 04两个版本的MySQL8 的安装 xff0c 和navicat的连接 其中ubuntu18 04对应第二节 xff0c ubuntu20 04是u
  • 对接淘宝公共平台API

    1 说明 由于项目临时提出需求 xff0c 需要对接淘宝公共平台查询用户的一些信息 xff0c 所以需要和淘宝平台做对接 xff0c 我查看了一下淘宝公共平台开发文档 xff0c 虽然写的挺丰富挺整洁 xff0c 但我还是一头雾水 xff0
  • 使用IDEA插件从数据库表生成实体类

    目录 1 介绍 2 添加插件 3 创建数据库连接 4 添加数据库连接信息和驱动 5 表生成实体类 1 介绍 EasyCode是基于IntelliJ IDEA Ultimate版开发的一个代码生成插件 xff0c 主要通过自定义模板 基于ve
  • SpringBoot多环境动态环境切换(nacos)

    目录 1 环境变量切换 1 1 建立各环境配置文件 1 2 设置环境变量 2 nacos配置中心动态切换 2 1 配置文件 2 2 nacos配置 2 3 启动服务 3 同一nacos环境下服务不同环境控制 3 1 cloud方式 3 1
  • 批量插入或更新数据(MyBatis-plus框架)

    目录 1 场景说明 2 DUPLICATE 和REPLACE比较 3 批量插入或者更新 两种方式 方式一 xff1a mybatis plus的saveOrUpdateBatch方法 问题 xff1a 如果操作类集成了基础类 xff0c 比
  • SpringBoot+Nacos+OpenFeign环境搭建

    目录 1 boot方式nacos与openFeign集成 1 引入依赖 2 添加配置 3 测试接口调用 4 常见问题 xff1a 1 版本依赖 2 nacos客户端 2 cloud方式nacos与openFeign集成 1 引入依赖 2 添
  • RestTemplate连接池使用

    说明 在调用淘宝的公共平台接口时候 xff0c 响应较慢 xff0c 我们需要60ms能够获取到响应 xff0c 但是却经常是200ms甚至更长时间 xff0c 别人的接口只能够优化网络响应时间来提升接口响应 由于接口并发量发 xff0c
  • 华为云CCI方式部署服务

    1 创建工作负载 说明 xff1a 创建负载使用的是swr自己上传的镜像 工作负载的创建过程相对简单 xff0c 和CCE类似 xff0c 创建好工作负载后会自动生成服务 2 配置路由 说明 xff1a 添加路由需要指定好容器端口和服务端口
  • RocketMQ单机环境搭建测试+springboot整合

    1 资源下载 官网 xff1a 下载 RocketMQ 这里选择使用编译后可以直接用的 下载后解压 xff1a 略 2 更改配置 主要是更改 conf broker conf 的配置 xff0c 记得添加上下面这几行 xff0c 否则消息发
  • Assignment 2: Exploratory Data Analysis

    Assignment 2 Exploratory Data Analysis 在此作业中 xff0c 您将识别出感兴趣的数据集并进行探索性分析 xff0c 以更好地理解数据的形状和结构 xff0c 调查最初的问题以及发展初步的见解和假设 您
  • jsp页面不显示的问题

    明明前后端的测试都写好了 xff0c 但是就是显示不出来数据 最后的 最后 发现是在引入js的时候一个小小的疏忽浪费了我半个小时的时间 xff0c 值得记录 一下
  • mysql group by 用法解析(详细)

    group by 用法解析 group by语法可以根据给定数据列的每个成员对查询结果进行分组统计 xff0c 最终得到一个分组汇总表 SELECT子句中的列名必须为分组列或列函数 列函数对于GROUP BY子句定义的每个组各返回一个结果
  • kubectl get pod卡住的问题

    安装minikube之后 xff0c 出现了kubectl get pod卡住的问题 xff0c 我这里主要网络的问题 xff0c 因为使用代理时没有过滤本地的IP xff0c 添加上过滤IP就可以用了 export no proxy 61
  • 记录Win10+Ubuntu18.04(引导Win10启动)双系统迁移到SSD,Ubuntu迁移成功但丢失Win10启动项

    原来的Win10 43 Ubuntu双系统是先装的Win10后装Ubuntu时选择 34 与Windows系统共存 34 xff0c 如此开机后由Ubuntu启动项 紫屏 接管引导进入Ubuntu或Windows系统 看网上的教程如果不dd
  • ubuntu 22.04部署quincy版ceph

    ceph集群安装配置有多种方式 xff0c 下方cephadm方式是借助容器部署 cephadm从ceph的octopus版本开始支持 安装需要主机配置安装了容器和python 3 配置安排 xff1a ceph版本 xff1a quinc
  • C/C++ 中头文件相互包含引发的问题

    今天下午遇到一个头文件相互包含而导致的编译问题 xff0c 花了我不少时间去调试没找到问题 xff0c 最后晚上跟师兄讨论不少时间 xff0c 突然有所顿悟 xff01 问题重现 我把问题脱离于项目简单描述一下 xff1a 我写了一个函数