专栏首页HeaiKunC语言---头文件

C语言---头文件

今天说一个我在工作时候发现的一个细节,可能大家都已经清楚的知道了,我就在这里记录一下吧。 不想看过程的直接去文末看结论吧。

声明一下:以下源文件就是.c文件,头文件就是.h文件。

编程规范规定,头文件里面不能include其他文件,只能在源文件中include使用到的头文件,防止头文件重复包含。对于我这种平时没这个规范习惯的人来说,就有疑问了。假如我有头文件a.h和b.h,a.h中定义了一个结构体数据类型,而我在b.h中是要使用这个数据类型的,一般情况我会直接在b.h这个头文件中包含a.h。这样就可以正常使用了,如果头文件不包含头文件的话,只在源文件中包含,这样能使用吗?会不会报错?

例如:源文件mian.c中包含了头文件a.h和b.h,头文件b.h中需要引用a.h中的数据结构类型。通过这种关系我自己做了一个检验。

a.h文件中定义了一个结构体类型,内容如下:

/*file: a.h*/
#ifndef __A_H__
#define __A_H__

typedef struct a{
    int a;
} A_S;

#endif

a.h定义了一个关于学生的一个结构体类型,然后我们需要在b.h中使用这个类型,但是b.h中不直接包含,内容如下:

/*file: b.h*/
#ifndef __B_H__
#define __B_H__

typedef struct b{
    int b;
    A_S sa;
}B_S;

#endif

b.h文件中定义了一个小组的结构体类型。SRU_S类型并没有在b.h中定义,也没有包含有定义此结构体的文件。然后我们在main.c中使用b.h中的结构。

main.c 内容如下:

/*file: main.c*/
#include <stdio.h>
#include "a.h"
#include "b.h"

int main()
{
    B_S sb;
    sb.b = 1;
    sb.sa.a = 99;

    printf("sb.b: %d\n sb.sa.a: %d\n", sb.b, sb.sa.a);
    return 0;
}

根据以上内容,直接进行gcc编译,是可以直接通过的:

hehk@hehk:~/test/C/test_include$ gcc main.c 
hehk@hehk:~/test/C/test_include$ ./a.out 
 sb.b: 1 
 sb.sa.a: 99 

如果我们main.c中的头文件包含a.h和b.h的位置调换一下,先包含b.h再包含a.h。如下所示:

#include "b.h"
#inlcude "a.h"

然后编译就报错了,提示如下:

hehk@hehk:~/test/C/test_include$ gcc main.c 
In file included from main.c:3:0:
b.h:8:5: error: unknown type name ‘A_S’
     A_S sa;
     ^~~
main.c: In function ‘main’:
main.c:10:7: error: request for member ‘a’ in something not a structure or union
  sb.sa.a = 99;
       ^
main.c:12:52: error: request for member ‘a’ in something not a structure or union
  printf(" sb.b: %d \n sb.sa.a: %d \n", sb. b, sb.sa.a);

由此可见,得出一下结论:

  1. 头文件中没有必要include其他头文件,可以都在源文件中进行include。
  2. 头文件被include的顺序很重要,被别的头文件引用的头文件一定要先include。

去了解一下编译过程就知道了,在编译过程中有一个环节是预编译,就是来处理以"#"开始的预编译指令,其中对#include的处理规则如下:

处理 “#include” 预编译指令,将被包含的文件插入到该预编译指令的位置,此过程是递归进行的,也就是说被包含的文件可能还包含其他文件。

由此可见,引入头文件跟我们定义变量差不多,要先定义然后才能在下面使用,这样就简单明了了。

本文分享自微信公众号 - HeaiKun(hhk-develop),作者:hehk

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 存储器层次结构介绍

    这里先说一下存储器系统: 寄存器 -----> 高速缓存 -----> 主存储 存储器对程序的性能有着巨大的影响,程序的运行就是对数据的不停的计算和搬移,其中最...

    HeaiKun
  • 是先学会游泳还是先下水?

    看到这句话,大家是不是都是反对这个观点的?对的,这个观点是错误的,但是这个错误的观点,在我们生活中却时时上演着。

    HeaiKun
  • CSAPP第三章(3)

    前面讲了汇编的基本语法,后面主要讲的就是 相对于C语言这样的高级语言中的一些语法结构进行汇编级别的解析。当然对于再复杂的C语言结构语法,最终都是那些简单的汇编语...

    HeaiKun
  • JDK源码分析-DelayQueue

    DelayQueue 也是一种队列,它内部的元素有“延迟”,也就是当从队列中获取元素时,如果它的延迟时间未到,则无法取出。

    WriteOnRead
  • Android类加载之PathClassLoader和DexClassLoader

    任何结论在没有经过实际检验之前都不能够确定一定没问题。三年前写的文章回过头来发现有些部分内容是有问题的(PS:这的确比较尴尬),再次对结果进行验证之后重新修改了...

    静默加载
  • 2-3 T-SQL函数

    在Transact-SQL语言中,函数被用来执行一些特殊的运算以支持SQL Server的标准命令。SQL Server包含多种不同的函数用以完成各种工作,每一...

    py3study
  • 梯度下降算法中的偏导公式推导

    前言:最近在跟着吴恩达老师(Andrew Ng)的视频课程学习机器学习,该视频是2014年拍的,虽然有点老,但理论却并不过时,是非常经典的机器学习入门教程,也正...

    阿波张
  • VIM编辑命令的技巧

     1 简单替换表达式 :%s/four/4/g “%” 范围前缀表示在所有行中执行替换。

    阳光岛主
  • Thinking--Normalize思想在前端中的应用

    发组件过程中,为了提高组件的灵活性,我们通常需要支持多种传参格式,如何优雅的控制和组件内部解耦变得尤为重要!

    奋飛
  • JavaScript模块化发展

    pitaojin

扫码关注云+社区

领取腾讯云代金券