C语言头文件到底是什么?

  • 在C语言学习的时候总是会引入这样的语句#include <stdio.h>,书上解释说把stdio.h这个文件的全部内容直接插入到这个位置,然后再经过C语言的编译器编译运行。这么看来隐含的意思好像是.h头文件好想并不直接参与编译。
  • 围绕这个话题引出了下面这几个问题。

一,.h头文件会参与编译吗?

  • 不妨来做个实验

这个是head.h文件的内容

#include <stdio.h>

int main() {
printf("Hello World!");
return 0;
}

这个是ori.c文件的内容

#include "head.h"

编译执行gcc ori.c -o ori

发现输出的是

>> .\ori.exe
>> hello world!

.c文件中并没有引入任何其他的文件,除了我们自己定义的head.h头文件,而在这个头文件中,我们引入了stdio.h头文件,并且我们在head.h里面定义的main函数被执行了,由此证明了include xxx.h是直接原封不同的插入到引用这个头文件的.c文件中的。

但是会参与编译吗?

为此设计下面这个实验:

  • 开一个项目,在.h头文件里面定义main函数,
  • 不引用这个头文件直接编译整个项目,然后执行,
  • 如果没有输出main函数内部的内容,那么表明如果不加引用,.h文件不参与编译。
  • 否则就参与编译。

这个是head.h文件内容

#include <stdio.h>

int main() {
printf("Hello World! I am head.h");
return 0;
}

这个是ori.c文件的内容

// nothing

编译执行gcc ori.c -o ori

发现输出的是

C:/Program Files/MinGW/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64
_libmingw32_a-crt0_c.o):crt0_c.c:(.text.startup+0x2e): undefined reference to `WinMain'
collect2.exe: error: ld returned 1 exit status

报错了,undefined reference to WinMain未定义WinMain

这时候我们在ori.c内部定义这样的一个函数

#include <stdio.h>

int WinMain() {
printf("Hello world! I am WinMain!");
return 0;
}

再次编译运行gcc ori.c -o ori

运行.\ori.exe

>> .\ori.exe
>> Hello world! I am WinMain!

发现正常输出了WinMain函数内部的内容,这也告诉我们WinMain函数同样可以作为程序的入口。

经过实验发现,的的确确.h绝对不是直接参与编译的,而是通过.c文件中#include "xxx.h"语句手动插入的。

这其实间接解答了在.h头文件中定义的静态局部变量无法局部化的原因。

二, .h头文件中的静态全局变量为什么可以被访问?

我们在学习C语言初期就直到,如果对一个全局变量使用static语句修饰的话,就可以把这个变量限制在本文件的访问域内,而无法被其他文件访问,但是这一点对于.h文件中无效,该访问还是可以访问?下面看一下实验

创建三个文件ori1.c, ori2.c, head.h

ori1.c中写下以下内容

#include <stdio.h>

extern int A;
extern int B; int main() {
printf("A = %d\n", A);
printf("B = %d", B);
return 0;
}

ori2.c中写下以下内容

#include <stdio.h>

int A = 12;
static B = 13;

此时在head.h中不写入任何内容

编译运行

正如和我们学过的一样,出现了未定义的错误

undefined reference to `B'
collect2.exe: error: ld returned 1 exit status

静态全局变量不可以被其他变量修改

这时候我们给ori2.h添加两个函数

void getB() {
printf("B = %d\n", B);
} void changeB(int num) {
B = num;
}

然后在ori1.c中去声明应用一下这两个函数

#include <stdio.h>

extern int A;

extern void getB();
extern void changeB(int B); int main() {
printf("A = %d\n", A);
getB();
changeB(1600);
getB();
return 0;
}

编译运行

>> A = 12
B = 13
B = 1600

虽然无法直接访问,但是可以定义ori2.c文件里面的函数去对该变量访问,这或许就是私有变量的雏形吧!

下面再来看看有关于.h中的全局变量,

由于我们已经知道了是完全插入的形式,那么我们可以理解到这个定义的静态全局变量是定义在引用它的文件里面

根据前文中定义在自己文件内部的静态全局变量只能被自己访问,那么结果自然显而易见了,

.h文件中的静态全局变量作用域是引用这个头文件的.c文件

自然而然的,我们应当明白,如果一个.h文件中定义过多的全局变量,那么这个全局变量被其他的变量引用的时候就会出现访问无指向的错误!重复定义

建议尽可能少在.h头文件中定义全局变量,或者静态全局变量

三,条件编译

C语言编译是把整个项目编译,而很多的.c文件都需要引用同一个函数,而正对于不同的系统又出现了不同的编译模式,如果说要把所有的.c文件头部都写入那些描述条件编译的代码,那么所有的代码写起来会复杂无比

所以把条件编译代码放在.h文件里面,直接配置好才是一个好的选择。

条件编译的例子

#include <stdio.h>
#include <stdlib.h> int main() {
#if _WIN64
system("color 0c");
printf("Hello World!");
#elif __linux__
printf("\033[22;31mHello World!\n\\033[22;30m\\");
#else
printf("Hello World!");
#endif
return 0;
}
#include <stdio.h>

int main() {
#if _WIN64
printf("This is Windows!\n");
#else
printf("Unknown platform!\n");
#endif #if __linux__
printf("This is Linux!\n");
#endif
return 0;
}

使用#ifdef判断宏是否被编译过,与之对应的还有一个#ifndef表示如果没有被定义的话、

#include <stdio.h>
#include <stdlib.h>
int main(){
#ifdef _DEBUG
printf("正在使用 Debug 模式编译程序...\n");
#else
printf("正在使用 Release 模式编译程序...\n");
#endif
system("pause");
return 0;
}

#if后面接的是整形常量表达式,而#ifdef后面只能接宏名

又由于自己可以定义宏,自然而然的自己就可以配置出自己的项目的条件编译。

四,一点建议

  1. 用的多的函数放在.h头文件中定义声明。
  2. 尽量不要在.h头文件中设置全局变量,或者静态全局变量。
  3. .h头文件中使用条件编译控制项目的编译,简化代码书写成本。

C语言头文件到底是什么?的更多相关文章

  1. 嵌入式C语言头文件的建立与使用

    如何正确编写 C 语言头文件和与之相关联的 c 源程序文件,这首先就要了解它们的各自功能. 要理解 C 文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程. 一般说来编译器会做以下几 ...

  2. 51单片机C语言学习笔记6:51单片机C语言头文件及其使用

    很多初学单片机者往往对C51的头文件感到很神秘,而为什么要那样写,甚至有的初学者喜欢问,P1口的P为什么要大写,不大写行不行呢?其实这个是在头文件中用sfr定义的,现在定义好了的是这样的 sfr P1 ...

  3. C语言头文件怎么写?(转载)

    ---恢复内容开始--- c语言头文件怎么写?我一直有这样的疑问,但是也一直没去问问到底咋回事:所以今天一定要把它弄明白! 其实学会写头文件之后可以为我们省去不少事情,可以避免书写大量的重复代码,还在 ...

  4. c语言头文件中定义全局变量的问题

    c语言头文件中定义全局变量的问题 (转http://www.cnblogs.com/Sorean/) 先说一下,全局变量只能定义在 函数里面,任意函数,其他函数在使用的时候用extern声明.千万不要 ...

  5. C语言头文件

    最近在工作当中遇到了一点小问题,关于C语言头文件的应用问题,主要还是关于全局变量的定义和声明问题.学习C语言已经有好几年了,工作使用也近半年了,但是对于这部分的东西的确还没有深入的思考过.概念上还是比 ...

  6. C++标准库头文件名字和C语言头文件名字的区别

    1.C++版本的C标准库头文件,一般是cname,而C语言头文件一般是name.h 2.命名为cname的头文件中定义的名字都是从std中来的,而如果是name.h则不是这样的. 3.与是用name. ...

  7. C语言头文件的使用(转载)

    C语言头文件的使用 ——by janders 转载请注名作者和出处,谢谢! C语言中的.h文件和我认识由来已久,其使用方法虽不十分复杂,但我却是经过了几个月的“不懂”时期,几年的“一知半解”时期才逐渐 ...

  8. C语言头文件、库文件的查找路径

    在 程序设计中,文件包含是很有用的.一个大的程序可以分为多个模块,由多个程序员分别编程.有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用.这样,可避免在每个 ...

  9. 用CBrother将excel中的数据转换为C语言头文件

    用CBrother将excel中的数据转换为C语言头文件 最近在工作中,产品这边总是要调整一些参数,而我们在这一块要求所有的配置必须用宏定义来做,因为不同型号直接硬编码写死在代码里,但是一但数量大了, ...

随机推荐

  1. Java开发工程师最新面试题库系列——Spring部分(附答案)

    Spring Spring框架是什么? 答:Spring是轻量级的面向切面和控制反转的框架.初代版本为2002年发布的interface21,Spring框架是为了解决企业级应用开发的复杂性的出现的, ...

  2. 绿色城市之地下综合管廊3D可视化平台

    前言 现阶段,我国绿色城市建设发展正在热火朝天的进行,面对迅速城镇化建设导致的城市病,需要不断寻求足以丰富城市的资源,以此实现城市绿色化智能化发展,比如改造地下管廊.路灯等城市基础设施. 地下综合管廊 ...

  3. ZooKeeper未授权访问漏洞确认与修复

    目录 探测2181 探测四字命令 用安装好zk环境的客户端连接测试 修复 修复步骤一 关闭四字命令 修复步骤二 关闭未授权访问 zookeeper未授权访问测试参考文章: https://www.cn ...

  4. c++指针数组与二维数组的最大区别

    下面随笔是关于指针数组说明及与二维数组的最大区别. 指针数组 数组的元素是指针型 例 利用指针数组存放矩阵 1 #include 2 using namespace std; 3 int main() ...

  5. C# smtp邮件发送

    第一种方式发送邮件,不读取配置文件发送邮件,(本地测试可以,但是服务器上不行) /// <summary> /// 发送邮件 /// </summary> /// <pa ...

  6. 漏洞复现-Discuz-命令执行(wooyun-2010-080723)

            0x00 实验环境 攻击机:win10 靶机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 Discuz 7.x 6.x版本 0x02 实验目的 学习d ...

  7. 浅谈.Net Core后端单元测试

    目录 1. 前言 2. 为什么需要单元测试 2.1 防止回归 2.2 减少代码耦合 3. 基本原则和规范 3.1 3A原则 3.2 尽量避免直接测试私有方法 3.3 重构原则 3.4 避免多个断言 3 ...

  8. Python3读取网页HTML代码,并保存在本地文件中

    旧版Python中urllib模块内有一个urlopen方法可打开网页,但新版python中没有了,新版的urllib模块里面只有4个子模块(error,request,response,parse) ...

  9. struct2中package的参数解析

    struct2框架的核心组件是action和拦截器,它使用包来管理action和拦截器,每个包就是多个action.多个拦截器引用的集合.在struct.xml中,package元素用于定义包的配置, ...

  10. css导航条的设计

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...