C语言头文件到底是什么?
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
后面只能接宏名
又由于自己可以定义宏,自然而然的自己就可以配置出自己的项目的条件编译。
四,一点建议
- 用的多的函数放在
.h
头文件中定义声明。- 尽量不要在
.h
头文件中设置全局变量,或者静态全局变量。- 在
.h
头文件中使用条件编译控制项目的编译,简化代码书写成本。
C语言头文件到底是什么?的更多相关文章
- 嵌入式C语言头文件的建立与使用
如何正确编写 C 语言头文件和与之相关联的 c 源程序文件,这首先就要了解它们的各自功能. 要理解 C 文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程. 一般说来编译器会做以下几 ...
- 51单片机C语言学习笔记6:51单片机C语言头文件及其使用
很多初学单片机者往往对C51的头文件感到很神秘,而为什么要那样写,甚至有的初学者喜欢问,P1口的P为什么要大写,不大写行不行呢?其实这个是在头文件中用sfr定义的,现在定义好了的是这样的 sfr P1 ...
- C语言头文件怎么写?(转载)
---恢复内容开始--- c语言头文件怎么写?我一直有这样的疑问,但是也一直没去问问到底咋回事:所以今天一定要把它弄明白! 其实学会写头文件之后可以为我们省去不少事情,可以避免书写大量的重复代码,还在 ...
- c语言头文件中定义全局变量的问题
c语言头文件中定义全局变量的问题 (转http://www.cnblogs.com/Sorean/) 先说一下,全局变量只能定义在 函数里面,任意函数,其他函数在使用的时候用extern声明.千万不要 ...
- C语言头文件
最近在工作当中遇到了一点小问题,关于C语言头文件的应用问题,主要还是关于全局变量的定义和声明问题.学习C语言已经有好几年了,工作使用也近半年了,但是对于这部分的东西的确还没有深入的思考过.概念上还是比 ...
- C++标准库头文件名字和C语言头文件名字的区别
1.C++版本的C标准库头文件,一般是cname,而C语言头文件一般是name.h 2.命名为cname的头文件中定义的名字都是从std中来的,而如果是name.h则不是这样的. 3.与是用name. ...
- C语言头文件的使用(转载)
C语言头文件的使用 ——by janders 转载请注名作者和出处,谢谢! C语言中的.h文件和我认识由来已久,其使用方法虽不十分复杂,但我却是经过了几个月的“不懂”时期,几年的“一知半解”时期才逐渐 ...
- C语言头文件、库文件的查找路径
在 程序设计中,文件包含是很有用的.一个大的程序可以分为多个模块,由多个程序员分别编程.有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用.这样,可避免在每个 ...
- 用CBrother将excel中的数据转换为C语言头文件
用CBrother将excel中的数据转换为C语言头文件 最近在工作中,产品这边总是要调整一些参数,而我们在这一块要求所有的配置必须用宏定义来做,因为不同型号直接硬编码写死在代码里,但是一但数量大了, ...
随机推荐
- 【Notes】现代图形学入门_02
跟着闫令琪老师的课程学习,总结自己学习到的知识点 课程网址GAMES101 B站课程地址GAMES101 课程资料百度网盘[提取码:0000] 光栅化 着色(Shading) 在图形学中,着色的定义可 ...
- CSS元素层级的概念及性质
元素的层级的介绍 什么是元素的层级 通过z-index可以改变开启定位元素的层级 父元素的层级再高也不会遮盖住子元素 元素的层级的介绍 什么是元素的层级 当元素开启定位后就会是元素提升一个层级,网页是 ...
- 【资源下载】Linux下的Hi3861一站式鸿蒙开发烧录(附工具)
下载附件 2021春节前夕,华为发布了 HUAWEI DevEco Device Tool 2.0 Beta1,整体提供了异常强大的功能.得知消息后,我在第一时间带着无比兴奋的心情下载尝鲜,但结果却是 ...
- AI在出行场景的应用实践:路线规划、ETA、动态事件挖掘…
前言:又到春招季!作为国民级出行服务平台,高德业务快速发展,大量校招/社招名额开放,欢迎大家投递简历,详情见文末.为帮助大家更了解高德技术,我们策划了#春招专栏#的系列文章,组织各业务团队的高年级同 ...
- 在ASP.NET Core中用HttpClient(一)——获取数据和内容
在本文中,我们将学习如何在ASP.NET Core中集成和使用HttpClient.在学习不同HttpClient功能的同时使用Web API的资源.如何从Web API获取数据,以及如何直接使用Ht ...
- MySQL全面瓦解24:构建高性能索引(策略篇)
学习如果构建高性能的索引之前,我们先来了解下之前的知识,以下两篇是基础原理,了解之后,对面后续索引构建的原则和优化方法会有更清晰的理解: MySQL全面瓦解22:索引的介绍和原理分析 MySQL全面瓦 ...
- 【vscode】【python】自定义代码自动补全
引自:https://blog.csdn.net/Kyrielong/article/details/88058884 { "python.jediEnabled": true, ...
- Python中树的遍历-堆排序
1.二叉树的遍历 遍历:迭代所有元素一遍. 树的遍历:对树中所有的元素不重复的访问一遍,也成扫描 广度优先遍历:层序遍历 深度优先遍历:前序.中序.后续遍历. 遍历序列:将树中所有元素遍历一遍后,得到 ...
- Java 多线程 | 并发知识问答总结
写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...
- python-for表达式
for表达式用于其他区间,元组,列表等可迭代对象创建新的列表 [表达式 for 循环计数器 in 可迭代对象] for表达式与普通for循环的区别有两点 在for关键字之前定义一个表达式,该表达式通常 ...