关于头文件是否参与编译的讨论
一、文章来由
写项目的时候发现了这个问题,又是一个比较底层的问题,首先说明,这篇文章只是我根据查阅的资料和做的实验提出的一个讨论,并不一定就是正确答案。因为这个问题网上众说纷纭,我很欢迎大家参与这个讨论,一起搞懂这个问题~~~
二、问题的提出
问题就是。。。
2.1 问题1(主问题):
头文件是否真正参与编译?
先上一个网上的标准答案:
.h的内容被插入到.c中,作为.c的内容被编译。.h文件本身不直接参加编译。
据我理解,这句话就是说明了头文件不直接参与编译,是作为一个插入来理解。
也就是说:
是要编译的,只不过这些头文件是预编译的。每个源文件包涵的头文件都会被预编译包含到源文件中去。
这样就又牵出来三个子问题~~
2.2 问题2:
预编译是什么?
网上的答案大致理解为类似复制粘贴的操作,这样理解是有理由的,因为
(1)头文件不一定是 .h 文件,可以是任意类型
(2)头文件可以定义一些很奇怪的东西,见下面的代码
// testheadcompile.h
1, 2, 3, 4, 5//main.cpp
#include <iostream>
using namespace std;int main()
{int a[] = {#include "testheadcompile.h"};cout<<a[1]<<endl;return 0;
}
分析:
这段代码可以说是真的变态,因为头文件写在了函数体内,这么说的确就像是一个复制粘贴,关键是可以跑出结果,我在vs2012 release模式下,结果如下:
而且还被360误认为是木马。。。
于是我这样认为:如果需要什么东西(变量或者方法)的时候,就直接可以像利用 #define 一样用 #include 了?而且#include,有井号本身就是宏的写法~~
但是发现,把 include 像上面一样写在方法体内,定义变量可以,函数不行,否则会报“本地函数定义是非法的”的错,但如果 include 在开头,就可以正常使用函数。这又是一个预处理的典型代表,说明了在函数里面定义了函数~~~
其实甚至可以写出这种鬼畜的代码:
// testheadcompile.h
1, 2, 3, 4//main.cpp
#include <iostream>
using namespace std;int main()
{int a[] = {#include "testheadcompile.h",5};cout<<a[4]<<endl;return 0;
}
答案是5
2.3 问题3:
既然上面说是预编译,或者说是单纯的复制,那么那些没有实现的函数预编译?编译?链接的时候编译器怎么做的?那些实现了,在整个过程没有用到的函数呢???
这个问题,我是这样看的:
所有的编译都是单独的,没有实现的函数,就没有单独实现的编译,如果用到了,找不到实现体,就会报链接错误,也就是vs里面常见的“fatal error LNKXXX: N 个无法解析的外部命令”。。。
而对于头文件的作用仅仅是为了在编译的时候告诉编译器,这里如果用到一些其他文件的东西,我实现了,编译别报错!
所以这么说,实现了,但在整个过程没有用到的函数,是编译了的,但没有被链接进最后的可执行文件。(可能有不对的地方,欢迎指正)
2.4 问题4:
既然说没有参与编译,只是参与预编译,但是可以在头文件里面定义函数,又如何解释?
首先我来回答一下这个问题,个人感觉是参与编译的,但是是头文件被包含到源文件进行编译,也就是说编译器只会编译源文件,不被包含的头文件是没有存在的意义的,因为我故意把头文件写错,然后这样报错。。。
刚刚测试发现了一个现象,没有cpp文件,vs也可以编译,我怀疑在编译的时候,默认将main函数所在的文件作为编译文件,即使main函数写在头文件,也可以正常执行。
而在Linux下面,用编译器编译时需要指定编译的文件,所以有没有后缀名是无关紧要的,反正都是文本文件,读取方式就是确定的,所以Linux对于后缀名并不重要。
关于头文件是否参与编译的讨论相关推荐
- C++头文件中预编译宏的目的
C++头文件中预编译宏的目的 eg: #ifndef _FACTORY_H_ #define _FACTORY_H_ ...... #endif //~_FACTORY_H_ 防止头文件被重复包含,导 ...
- C++ : 编译单元、声明和定义、头文件作用、防止头文件在同一个编译单元重复引用、static和不具名空间...
转 自:http://www.cnblogs.com/rocketfan/archive/2009/10/02/1577361.html 1. 编译单元:一个.cc或.cpp文件作为一个编译单元,生成 ...
- glibc的头文件 linux_求助,编译glibc头文件时出错
我用的软件包如下: binutils-2.16.tar.gz gcc-3.4.4.tar.bz2 glibc-2.3.5.tar.gz glibc-linuxthreads-2.3.5.tar.gz ...
- 学习OpenCV时 ,添加:#includeopencv2/core/core.hpp等头文件出现无法编译的错误
在使用win7+vs2010+opencv2.4.8时 经常在刚创建完项目--->源文件之后 ,添加头文件:#include<opencv2/core/core.hpp> ...
- C++控制台没有引用的头文件也会编译的原因
导致原因:虽然主源文件没有引用相关的文件,而项目有包括这些文件到项目中.故而也会编译. 转载于:https://www.cnblogs.com/songtzu/archive/2013/01/12/2 ...
- Android9.0 HIDL头文件加log编译
1.编译android.hardware.audio.core@all-versions-impl # 编译之前必须删除,不然不会生成新的android.hardware.audio.core@all ...
- CCS编译出错:缺少头文件的解决办法
问题: "../Source/DSP2833x_SysCtrl.c", line 16: fatal error #5: could not open source file &q ...
- Linux下gcc编译中关于头文件与库文件搜索路径相关问题
如何指定GCC的默认头文件路径 网上偶搜得之,以之为宝:) 原地址:http://blog.chinaunix.net/u/28781/showart.php?id=401631 ========== ...
- 【Android 高性能音频】Oboe 开发流程 ( 导入 Oboe 库 | 使用预构建的二进制库和头文件 | 编译 Oboe 源码 )
文章目录 一.导入 Oboe 库 二.使用预构建的二进制库和头文件 三.编译 Oboe 源代码 Oboe GitHub 主页 : GitHub/Oboe ① 简单使用 : Getting Starte ...
最新文章
- puppet(1.7-2.1)
- golang float string int 相互转换 保留小数位
- spring boot 表单的实体提交错误:Validation failed for object='book'. Error count: 2
- Visual Studio 2017 - Update 2预览版已发布
- spring bean的创建,生命周期
- printstream_Java PrintStream close()方法与示例
- 关于mysql优化_关于MySQL优化的几点总结
- django-用户文件的上传-后台上传
- Idea日常使用记录
- JSP中contentType和pageEncoding的区别
- linux如何查看jupyter日志_在Linux服务器上运行Jupyter notebook server教程
- Toolbar的简单使用和封装
- v4l2API无法执行VIDIOC_DQBUF的问题
- 2013元旦成都九寨沟攻略
- C 语言实例 - 判断奇数/偶数
- abaqus算出来的转角单位是什么_ABAQUS中的单位使用方法
- 怎么使用openssl来生成一个自签名的x509证书?
- 最简单易懂的10堂算法入门课——算法是什么
- 二叉搜索树插入算法C#演示的代码
- 数独题 HDU - 1426
热门文章
- sox处理mp3_音频处理常用Linux命令总结(一)
- java 迭代器的原理_java里Iterator的原理
- dvwa安装包linux,dvwa安裝、配置、使用教程(Linux)
- 图像语义分割_图像语义分割(9)-DeepLabV3: 再次思考用于图像语义分割的空洞卷积...
- android从服务检查,android开发分享Android:你如何检查是否启用了特定的AccessibilityService...
- linux可用的ftp,linuxunix下有很多可用的ftp服务器
- php判断url参数为空,PHP检查url链接是否已经有参数的简单示例
- oracle数据库配置管理,Oracle配置管理
- nodejs+php+aes加密解密,php,crypto_php与nodejs的加密数据互通,php,crypto,node.js - phpStudy...
- 1445.32php,nginx实现mysql的负载均衡