C/CPP 入门指南 - 正确理解 C++ 中的头文件的写法

本文详细解析了C/C++中宏指令#include的工作机制及其在预处理阶段的作用,并深入探讨了头文件中#ifndef、#define、#endif等指令的正确用法,避免因头文件嵌套导致的重复定义问题。

前言


在 C/CPP 中,按照模块来组织一个 Project,会让 Project 更容易阅读和维护。

正确理解宏指令 #include 的处理机制


“#include” 宏指令是在正式编译之前的预处理程序(preprocessing)来处理的,处理方法和 “include” 的英文意思一样:文件包含 - 将指定文件嵌入到指定位置。例如,我们有以下两个文件:DemoInclude.h、DemoInclude.cpp,内容如下:

DemoInclude.h

#ifndef __DEMOINCLUDE_H__
#define __DEMOINCLUDE_H__
void DemoFunc(void);
#endif

DemoInclude.cpp

#include "DemoInclude.h"

int main(void) {
	DemoFunc();
	return 0;
}

经过预处理(preprocessing)后,以上程序片段被处理为如下程序片段:

#ifndef __DEMOINCLUDE_H__
#define __DEMOINCLUDE_H__
void DemoFunc(void);
#endif

int main(void) {
	DemoFunc();
	return 0;
}

经过预处理程序,头文件会被真正的包含进 #include 所在的位置!

正确理解头文件中 #ifndef#define#endif 的用法


头文件的标准结构通常如下:

#ifndef __头文件文件名__
#define __头文件文件名__
// 头文件的具体内容
#endif

Q:为什么要使用上述的标准结构呢?

A:这样的标准结构是为了避免 “头文件嵌套” 带来的重复定义问题。

例如,看以下程序片段,有三个文件:DemoH1.h、DemoH2.h、Demo.cpp

DemoH1.h

void DemoFunc1(void);

DemoH2.h

#include "DemoH1.h"
void DemoFunc2(void);

Demo.cpp

#include "DemoH1.h"
#include "DemoH2.h"

int main(void) {
	DemoFunc1();
	DemoFunc2();
	return 0;
}

根据上节的讲解,我们知道,经过预处理之后,以上程序片段会被处理为如下程序片段:

// #include "DemoH1.h" 被处理为:
void DemoFunc1(void);
// #include "DemoH2.h" 被处理为:
void DemoFunc1(void);
void DemoFunc2(void);

int main(void) {
	DemoFunc1();
	DemoFunc2();
	return 0;
}

很显然,在上面的程序片段中:DemoFunc1() 被重复定义了两次(在第 2 行和第 4 行),编译器会报告重复定义错误。

如果我们按照标准结构改写 DemoH1.h、DemoH2.h,经过预处理程序,以上程序片段会被处理为如下程序片段:

#ifndef __DEMOH1_H__
#define __DEMOH1_H__
void DemoFunc1(void);
#endif

#ifndef __DEMOH1_H__
#define __DEMOH1_H__
void DemoFunc1(void);
#endif

#ifndef __DEMOH2_H__
#define __DEMOH2_H__
void DemoFunc2(void);
#endif

int main(void) {
	DemoFunc1();
	DemoFunc2();
	return 0;
}

接下来,预处理程序将处理 #ifndef#define#endif 宏指令:

  • 第 1 行 #ifndef __DEMOH1_H__:标识符(identifier)__DEMOH1_H__ 是否被定义?如果没有定义,则直到 #endif 之前的代码块有效。很显然,__DEMOH1_H__ 从来都没有出现过,所以代码段 2-3 行有效
  • 第 2 行 #define __DEMOH1_H__:定义标识符 __DEMOH1_H__(注意:__DEMOH1_H__可以为空)。
  • 第 6 行 #ifndef __DEMOH1_H__:标识符 __DEMOH1_H__ 是否被定义?如果没有定义,则直到 #endif 之前的代码块有效。很显然,__DEMOH1_H__ 在第 2 行被定义了,所以代码段 7-8 行无效

经过以上预处理过程,以上代码片段会被处理为如下代码片段:

// #ifndef __DEMOH1_H__
#define __DEMOH1_H__
void DemoFunc1(void);
// #endif
//
// #ifndef __DEMOH1_H__
// #define __DEMOH1_H__
// void DemoFunc1(void);
// #endif
//
// #ifndef __DEMOH2_H__
#define __DEMOH2_H__
void DemoFunc2(void);
// #endif
int main(void) {
	DemoFunc1();
	DemoFunc2();
	return 0;
}

这时,我们发现 DemoFunc() 只被定义了一次,不会出现编译错误。

Q:#ifndef identifier 中的标识符(identifier)为什么要使用 __文件名__ 的形式,可以使用其它形式的标识符吗?

A:从上一节的分析来看,我们只需要求 #ifndef identifier#define identifier 成对出现就可以避免重复定义的问题,因此原则上说:identifier 可以是任意形式。但是,要注意的是:不同头文件中的 identifier 不能相同,否则头文件嵌套时,会出现缺失定义的问题(可以自行分析下)。当 Project 很大的时候,如果不按照一定的规则,很难保证不同头文件中的 identifier 唯一。习惯上使用 __文件名__ 的形式是因为:同一个 Project 中肯定不会出现同名的头文件,因此很简单的保证了 identifier 唯一的要求。

FAILED: CMakeFiles/show.dir/src/show.cpp.o /usr/bin/c++ -DDEFAULT_RMW_IMPLEMENTATION=rmw_fastrtps_cpp -DRCUTILS_ENABLE_FAULT_INJECTION -isystem /opt/ros/humble/include/rclcpp -isystem /opt/ros/humble/include/sensor_msgs -isystem /opt/ros/humble/include/cv_bridge -isystem /usr/include/opencv4 -isystem /opt/ros/humble/include/ament_index_cpp -isystem /opt/ros/humble/include/libstatistics_collector -isystem /opt/ros/humble/include/builtin_interfaces -isystem /opt/ros/humble/include/rosidl_runtime_c -isystem /opt/ros/humble/include/rcutils -isystem /opt/ros/humble/include/rosidl_typesupport_interface -isystem /opt/ros/humble/include/fastcdr -isystem /opt/ros/humble/include/rosidl_runtime_cpp -isystem /opt/ros/humble/include/rosidl_typesupport_fastrtps_cpp -isystem /opt/ros/humble/include/rmw -isystem /opt/ros/humble/include/rosidl_typesupport_fastrtps_c -isystem /opt/ros/humble/include/rosidl_typesupport_introspection_c -isystem /opt/ros/humble/include/rosidl_typesupport_introspection_cpp -isystem /opt/ros/humble/include/rcl -isystem /opt/ros/humble/include/rcl_interfaces -isystem /opt/ros/humble/include/rcl_logging_interface -isystem /opt/ros/humble/include/rcl_yaml_param_parser -isystem /opt/ros/humble/include/libyaml_vendor -isystem /opt/ros/humble/include/tracetools -isystem /opt/ros/humble/include/rcpputils -isystem /opt/ros/humble/include/statistics_msgs -isystem /opt/ros/humble/include/rosgraph_msgs -isystem /opt/ros/humble/include/rosidl_typesupport_cpp -isystem /opt/ros/humble/include/rosidl_typesupport_c -isystem /opt/ros/humble/include/geometry_msgs -isystem /opt/ros/humble/include/std_msgs -g -fdiagnostics-color=always -MD -MT CMakeFiles/show.dir/src/show.cpp.o -MF CMakeFiles/show.dir/src/show.cpp.o.d -o CMakeFiles/show.dir/src/show.cpp.o -c /home/sfx233/yinyou/src/answer/src/show.cpp /home/sfx233/yinyou/src/answer/src/show.cpp:7:9: fatal error: opencv4/opencv.hpp: 没有那个文件或目录 7 | #include"opencv4/opencv.hpp"
03-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值