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. Content type 'application/json;charset=UTF-8' not supported异常的解决过程

    首先说一下当时的场景,其实就是一个很简单的添加操作,后台传递的值是json格式的,如下图 ,后台对应的实体类, @Data @EqualsAndHashCode(callSuper = false) ...

  2. MySQL 事务的隔离级别

    转载:https://developer.aliyun.com/article/743691?accounttraceid=80d4fddb3dc64b97a71118659e106221tozz 简 ...

  3. HBase 数据存储结构

    在HBase中, 从逻辑上来讲数据大概就长这样: 单从图中的逻辑模型来看, HBase 和 MySQL 的区别就是: 将不同的列归属与同一个列族下 支持多版本数据 这看着感觉也没有那么太大的区别呀, ...

  4. Linux graphics stack

    2D图形架构 早期Linux图形系统的显示全部依赖X Server,X Client调用Xlib提供的借口向 X Server发送渲染命令,X Server根据 X Client的命令请求向硬件设备绘 ...

  5. mysql 单表下的字段操作

    如下只介绍单表的添加.更新.删除.查询表结构操作,查询数据操作范围太大用单独的篇幅来讲解: 查看表结构 desc test_tb; Insert 插入数据 插入 = 添加 为表中指定的字段插入数据 C ...

  6. MySql学习---数据库基本类型,事务,多表查询

    数据库分类 关系型数据库 行列, 列如Mysql,oracle 通过表和表之间,行和列之间的关系进行数据的存储 非关系型数据库: Redis,MongDb 以对象存储,同过对象的自身属性来决定 表与表 ...

  7. 致被职场PUA的打工人

    作为打工人,除了每天面对着各种繁琐的工作,还要被动接受上级或多或少的PUA,实在是难上加难,甚至有人想不开而自杀.网络上最近流行了一个词:职场PUA,赋予了这种现象一个正式的名字. 职场PUA指的是职 ...

  8. 谈谈注册中心 zookeeper 和 eureka中的CP和 AP

    谈谈注册中心 zookeeper 和 eureka中的CP和 AP 前言 在分布式架构中往往伴随CAP的理论.因为分布式的架构,不再使用传统的单机架构,多机为了提供可靠服务所以需要冗余数据因而会存在分 ...

  9. python基础学习之简化占位符和格式化的概念

    简化版占位符有哪些? %s   字符串占位符,代表该位置有一个字符串待替换(万能) %d   数字占位符,代表该位置有一个数字待替换 %f    同上,这里是浮点数,float[默认保留6位小数] % ...

  10. FHRP - 网关冗余协议

    通常情况下,在终端设备进入网络前,都会有一个 Router 充当网络,作为第一跳的网络地址.但假设路由器发生故障,此时终端设备就无法再接入互联网. 为了防止这样的问题,一般会再加入一台路由器充当备份. ...