C语言18--头文件

news2024/9/19 10:32:09

头文件的作用

通常,一个常规的C语言程序会包含多个源码文件(.c),当某些公共资源需要在各个源码文件中使用时,为了避免多次编写相同的代码,一般的做法是将这些大家都需要用到的公共资源放入头文件(.h)当中,然后在各个源码文件中直接包含即可。

头文件的内容

  • 头文件中所存放的内容,就是各个源码文件的彼此可见的公共资源,包括:
  1. 全局变量的声明。
  2. 普通函数的声明。
  3. 静态函数的定义。
  4. 宏定义。
  5. 结构体、联合体的声明。
  6. 枚举常量列表的声明。
  7. 其他头文件。

示例代码:

// head.h
extern int global; // 1,全局变量的声明
extern void f1();  // 2,普通函数的声明
static void f2()   // 3,静态函数的定义
{
    ...
}
#define MAX(a, b) ((a)>(b)?(a):(b)) // 4,宏定义
struct node    // 5,结构体的定义
{
    ...
};
union attr    // 6,联合体的定义
{
    ...
};
#include <unistd.h> // 7,其他头文件
#include <string.h>
#include <stdint.h>
  • 特别说明:
    1. 全局变量、普通函数的定义一般出现在某个源文件(*.c *.cpp)中,其他的源文件想要使用都需要进行声明,因此声明语句一般放在头文件中更方便。
    2. 静态函数、宏定义、结构体、联合体的声明都只能在其所在的文件可见,因此如果多个源文件都需要使用的话,放到头文件中定义是最方便,也是最安全的选择。

头文件的使用

头文件编写好了之后,就可以被各个所需要的源码文件包含了,包含头文件的语句就是如下预处理指令:

// main.c
#include "head.h"  // 包含自定义的头文件
#include <stdio.h> // 包含系统预定义的文件

int main()
{
    ...
}

头文件包含的细节:

#include <stdio.h>  
/*
#include <...> search starts here:   <>默认从以下系统路径寻找
 /usr/lib/gcc/x86_64-linux-gnu/11/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

*/

#include "./inc/myHead.h"  // 使用双引号包含的头文件会从当前代码所处的路径下搜索
// 由于一般我们自己写的头文件会于项目存放在同一个路径下
// 因此一般会使用双引号来包含自己(非系统的头文件)编写的头文件
// 以上例子中有写该头文件的详细路径,因此编译时直接gcc 即可


#include "myHead.h" // 没有写明头文件的具体相对路径
// 因此编译时需要告知编译器被你存储与哪个文件
gcc  xxx.c   -I./inc 
-I 用于告知编译器头文件的路径
./inc  则是具体的路径值

可以看到,在源码文件中包含指定的头文件有两种不同的形式:

  • 使用双引号:在指定位置 + 系统标准路径搜索 head.h
  • 使用尖括号:在系统标准路径搜索 stdio.h

由于自定义的头文件一般放在源码文件的周围,因此需要在编译的时候通过特定的选项来指定位置,而系统头文件都统一放在标准路径下,一般无需指定位置。

假设在源码文件 main.c 中,包含了两个头文件:head.h 和 stdio.h ,由于他们一个是自定义头文件,一个是系统标准头文件,前者放在项目 pro/inc 路径下,后者存放于系统头文件标准路径下(一般位于 /usr/include),因此对于这个程序的编译指令应写作:

gec@ubuntu:~/pro$ gcc main.c -o main -I./inc

头文件的格式

由于头文件包含指令 #include 的本质是复制粘贴,并且一个头文件中可以嵌套包含其他头文件,因此很容易出现一种情况是:头文件被重复包含。

  • 使用条件编译,解决头文件重复包含的问题,格式如下:
    • 作用:防止头文件被多次重复包含后出现重复定义的问题
#ifndef _HEADNAME_H   
#define _HEADNAME_H

...
... (头文件正文)
...

#endif

其中,HEADNAME一般取头文件名称的大写

项目的基本框架:

编译命令:

gcc src/*.c -o bin/demo -I./inc

gcc         #编译命令
src/*.c     #需要编译的源文件 *.c 表示该路径下的所有.c文件
-o          #指明输出的文件名字
bin/demo    #具体输出的可执行文件路径+名字
-I./inc     # -I指定头文件的路径 ./inc 具体的路径

C语言的关键字:

在C语言中,以下是一些关键字及其作用:

1. #include:预处理指令,用于包含头文件。例如:#include <stdio.h> 包含头文件 stdio.h,以便使用其中的函数。

2. #define:预处理指令,用于定义宏。例如:#define MAX 1000 定义一个宏 MAX,值为 1000。

3. #undef:预处理指令,用于取消定义宏。例如:#undef MAX 取消定义宏 MAX。

4. #ifdef:预处理指令,用于检查当前定义的宏是否已定义。例如:#ifdef MACRO 检查是否定义了宏 MACRO。

5. #ifndef:预处理指令,用于检查当前定义的宏是否未定义。例如:#ifndef MACRO 检查是否未定义宏 MACRO。

6. #else:预处理指令,用于实现条件编译。例如:#ifdef MACRO #else 表示当宏 MACRO 已定义时执行 #else 中的代码。

7. #elif:预处理指令,用于实现条件编译的 elif 分支。例如:#ifdef MACRO #elif MACRO2 表示当宏 MACRO 已定义时执行 #elif MACRO2 中的代码。

8. #endif:预处理指令,用于结束条件编译。例如:#endif 结束条件编译。

9. #error:预处理指令,用于生成错误信息。例如:#error "Error message" 生成错误信息 "Error message"。

10. #pragma:预处理指令,用于提供编译器特定的信息。例如:#pragma pack(1) 告诉编译器使用大小为 1 的对齐方式。

11. #volatile:预处理指令,用于指示对变量的访问应该使用寄存器方式。例如:#volatile 修饰的变量在汇编语言中不会被优化。

12. #restrict:预处理指令,用于提示编译器使用 restrict 属性。例如:#restrict int *a 修饰的指针 a,编译器会使用 restrict 属性。

13. auto:关键字,用于指定变量为自动变量。例如:auto int a 声明一个自动变量 a。

14. static:关键字,用于指定变量为静态变量。例如:static int a 声明一个静态变量 a。

15. register:关键字,用于指定变量为寄存器变量。例如:register int a 声明一个寄存器变量 a。

16. volatile:关键字,用于指定变量为 volatile 变量。例如:volatile int a 声明一个 volatile 变量 a。

17. const:关键字,用于指定变量为常量。例如:const int a 声明一个常量变量 a。

18. inline:关键字,用于指定函数为内联函数。例如:inline void func() { ... } 声明一个内联函数 func。

19. extern:关键字,用于指定变量为外部变量。例如:extern int a 声明一个外部变量 a。

20. __cdecl、__stdcall、__fastcall、__thiscall:调用约定,用于指定函数的调用方式。例如:__cdecl void func() { ... } 声明一个使用 __cdecl 调用约定函数 func。

21. __attribute__:属性,用于指定函数的属性,如限制参数数量、设置栈大小等。例如:__attribute__((stdcall)) void func(int arg) { ... } 声明一个使用 stdcall 调用约定函数 func,并限制参数数量为 int arg。

22. __packed:属性,用于指定变量或结构体的对齐方式。例如:__packed int a 声明一个使用 __packed 属性变量 a。

23. __section:属性,用于指定变量或结构体放置的节区。例如:__section(".data") int a 声明一个使用 __section(".data") 属性变量 a。

24. __alignof__:运算符,用于获取变量或类型的对齐字节数。例如:__alignof__(int) 获取 int 类型的对齐字节数。

25. __builtin_offsetof:函数,用于获取结构体中成员的偏移量。例如:__builtin_offsetof(struct my_struct, my_member) 获取 struct my_struct 中的 my_member 成员的偏移量。

26. __has_feature:宏,用于检查编译器是否具有某些特性。例如:__has_feature(c++11) 检查编译器是否具有 c++11 特性。

27. __is_available:宏,用于检查某个特性是否可用。例如:__is_available("avx") 检查 avx 特性是否可用。

28. __clang_builtin_macro:宏,用于检查某个宏是否为 clang 内置宏。例如:__clang_builtin_macro("__clang_max__") 检查 __clang_max__ 是否为 clang 内置宏。

29. __GNUC_MINOR__:宏,用于获取 gcc 版本的小版本号。例如:__GNUC_MINOR__ 获取 gcc 的 minor 版本号。

30. __GNUC_PATCHLEVEL__:宏,用于获取 gcc 版本的补丁版本号。例如:__GNUC_PATCHLEVEL__ 获取 gcc 的 patchlevel 版本号。

结语:

        在这篇博客中,我们详细探讨了C语言中头文件的概念及其重要性。头文件作为代码组织的一部分,不仅便利了函数和变量的声明,也使得不同源文件之间的协作变得更加高效。通过合理地使用头文件,我们能够模块化代码,提高可读性和可维护性。

        此外,了解如何创建和管理自定义头文件,能够帮助我们减少代码重复,提升项目的结构性。在实际开发中,正确使用头文件可以降低错误的发生率,并使调试过程更加顺畅。

        希望这篇文章能够帮助您深入理解C语言中的头文件,以及如何在项目中有效地使用它们。随着编程技能的提升,对代码结构和组织方式的重视将为您的开发工作带来长远的好处。感谢您的阅读,期待与您在今后的讨论中共同分享和学习更多编程知识!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2146098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

光学超表面在成像和传感中的应用

光学超表面已成为解决笨重光学元件所带来的限制&#xff0c;极具前景的解决方案。与传统的折射传播技术相比&#xff0c;它们提供了一种紧凑、高效的光操纵方法&#xff0c;可对相位、偏振和发射进行先进的控制。本文概述了光学超表面、它们在成像和传感技术中的各种应用以及这…

Broadcast:Android中实现组件与进程间通信

目录 一&#xff0c;Broadcast和BroadcastReceiver 1&#xff0c;简介 2&#xff0c;广播使用 二&#xff0c;静态注册和动态注册 三&#xff0c;无序广播和有序广播 1&#xff0c;有序广播的使用 2&#xff0c;有序广播的截断 3&#xff0c;有序广播的信息传递 四&am…

力扣(LeetCode)每日一题 1184. 公交站间的距离

题目链接https://leetcode.cn/problems/distance-between-bus-stops/description/?envTypedaily-question&envId2024-09-16 环形公交路线上有 n 个站&#xff0c;按次序从 0 到 n - 1 进行编号。我们已知每一对相邻公交站之间的距离&#xff0c;distance[i] 表示编号为 i …

Python燃烧废气排放推断算法模型

&#x1f3af;要点 宏观能耗场景模型参数化输入数据&#xff0c;分析可视化输出结果&#xff0c;使用场景时间序列数据模型及定量和定性指标使用线图和箱线图、饼图、散点图、堆积条形图、桑基图等可视化模型输出结果根据气体排放过程得出其时间序列关系&#xff0c;使用推断模…

nginx基础篇(一)

文章目录 学习链接概图一、Nginx简介1.1 背景介绍名词解释 1.2 常见服务器对比IISTomcatApacheLighttpd其他的服务器 1.3 Nginx的优点(1)速度更快、并发更高(2)配置简单&#xff0c;扩展性强(3)高可靠性(4)热部署(5)成本低、BSD许可证 1.4 Nginx的功能特性及常用功能基本HTTP服…

GlusterFS 分布式文件系统

一、GlusterFS 概述 1.1 什么是GlusterFS GlusterFS 是一个开源的分布式文件系统&#xff0c;它可以将多个存储服务器结合在一起&#xff0c;创建一个大的存储池&#xff0c;供客户端使用。它不需要单独的元数据服务器&#xff0c;这样可以提高系统的性能和可靠性。由于没有…

python毕业设计基于django+vue医院社区医疗挂号预约综合管理系统7918h-pycharm-flask

目录 技术栈和环境说明预期达到的目标具体实现截图系统设计Python技术介绍django框架介绍flask框架介绍解决的思路性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示操作可行性技术路线感恩大学老师和同学详细视频演示源码获取 技术…

【Finetune】(二)、transformers之Prompt-Tuning微调

文章目录 0、prompt-tuning基本原理1、实战1.1、导包1.2、加载数据1.3、数据预处理1.4、创建模型1.5、Prompt Tuning*1.5.1、配置文件1.5.2、创建模型 1.6、配置训练参数1.7、创建训练器1.8、模型训练1.9、推理&#xff1a;加载预训练好的模型 0、prompt-tuning基本原理 prompt…

【机器学习】任务五:葡萄酒和鸢尾花数据集分类任务

目录 1.实验基础知识 1.1 集成学习 &#xff08;1&#xff09;随机森林 &#xff08;2&#xff09;梯度提升决策树&#xff08;GBDT&#xff09; &#xff08;3&#xff09;XGBoost &#xff08;4&#xff09;LightGBM 1.2 参数优化 &#xff08;1&#xff09;网格搜索…

编写第一个hadoop3.3.6的mapreduce程序

hadoop还是用的上个伪分布环境。 hadoop安装在龙蜥anolis8.9上&#xff0c;开发是在windows下。 1、windows下首先要下载hadoop的包&#xff0c;hadoop-3.3.6.tar.gz&#xff0c;比如我的解压到d:\java\hadoop-3.3.6中。 配置环境&#xff1a;HADOOP_HOME&#xff0c;内容为&am…

ava总结篇系列:Java泛型Java sort用法详解

一. 泛型概念的提出&#xff08;为什么需要泛型&#xff09;&#xff1f; 首先&#xff0c;我们看下下面这段简短的代码: 1 public class GenericTest { 2 3 public static void main(String[] args) { 4 List list new ArrayList(); 5 list.add(&q…

【Elasticsearch系列四】ELK Stack

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

mysql事务的隔离级别学习

事务的隔离级别: ⅰ. 读未提交 ⅱ. 对已提交 &#xff08;解决 脏读&#xff09; ⅲ. 可重复读 &#xff08;解决 不可重复读&#xff09; ⅳ. 串行化 &#xff08;解决 脏读 不可重复读 幻读 问题 &#xff09; 隔离级别分类如下&#xff0c;在不同的隔离级别下可能产生不…

网络安全。

文章目录 目录 文章目录 一. 网络安全概述 二. 密码学原理 三. 报文完整性和数字签名 密码散列函数 报文鉴别码 数字签名 公钥认证 四. HTTPS通信 总结 一. 网络安全概述 网络安全是保护计算机网络及其数据免受各种威胁和攻击的实践和技术。随着互联网的普及和数字化…

Linux shell编程学习笔记81:zcat命令——快速查看压缩文件内容

0 引言 在 Linux shell编程学习笔记80&#xff1a;gzip命令——让文件瘦身-CSDN博客https://blog.csdn.net/Purpleendurer/article/details/141862213?spm1001.2014.3001.5501中&#xff0c;我们使用gzip命令可以创建压缩文件。那么&#xff0c;我们可以使用zcat命令来查看压…

传输层协议——udp/tcp

目录 再谈端口号 udp 协议 理解报头 udp特点 缓冲区 udp使用的注意事项 tcp协议 TCP的可靠性与提高效率的策略 序号/确认序号 窗口大小 ACK&#xff1a; PSH URG RST 保活机制 重传 三次握手(SYN) 四次挥手(FIN) 流量控制 滑动窗口 拥塞控制 延迟应答 捎带应答 面…

JavaScript match() 方法

match() 方法可在字符串内检索指定的值&#xff0c;或找到一个或多个正则表达式的匹配。 如果想了解更多正则表达式教程请查看&#xff1a; RegExp 教程 和我们的 RegExp 对象参考手册。 注意&#xff1a; match() 方法将检索字符串 String Object&#xff0c;以找到一个或多个…

Vue3 项目引入阿里 iconfont 图标和字体的多种方式

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

计算机人工智能前沿进展-大语言模型方向-2024-09-19

计算机人工智能前沿进展-大语言模型方向-2024-09-19 1. SAM4MLLM: Enhance Multi-Modal Large Language Model for Referring Expression Segmentation Authors: Yi-Chia Chen, Wei-Hua Li, Cheng Sun, Yu-Chiang Frank Wang, Chu-Song Chen SAM4MLLM: 增强多模态大型语言模型…

Java面试篇基础部分-Java线程生命周期

线程的生命周期分别为 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)这五种状态。   在系统运行过程中有线程不断地被创建,而旧的线程在执行完毕之后被清理,线程通过排队的方式获取共享资源或者锁的时候被阻塞,所以运行中的线程就会在…