📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨
📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】
📢:文章若有幸对你有帮助,可点赞 👍 收藏 ⭐不迷路🙉
📢:内容若有错误,敬请留言 📝指正!原创文,转载请注明出处
文章目录
一、源文件与头文件的概念
C语言中源文件的概念:
源文件即源代码所在文件,后缀名为.c。因此只要以.c为后缀名的文件都是C语言的源文件。
什么是头文件?
使用#include引入的文件,以.h为后缀名的文件。
一般地,在C语言或C++中,会把用来#include的文件的扩展名叫.h,称其为头文件。#include文件的目的就是把多个编译单元(也就是c或者cpp文件)公用的内容,单独放在一个文件里减少整体代码尺寸;或者提供跨工程公共代码。
在现行的c版本中,应用这个头文件应是#include<stdio.h>。
二、头文件的作用
1、声明函数和变量
头文件主要用于提供函数和变量的声明,以供其他源代码文件引用和使用。头文件中可以包含函数和变量的声明、结构体的定义、宏定义以及其他需要在不同源文件之间共享的元素。其他源代码文件可以通过包含(include)头文件来访问其中声明的函数和变量,从而可以在自己的代码中使用这些函数和变量,而无需重新编写它们的定义。
🙉思考:头文件里面可以定义函数吗?
答案:在头文件中,可以对函数进行声明,但是不应该进行函数的定义。
因为头文件的主要作用是提供接口和声明,以便其他源文件可以访问和使用。
因此函数的定义应该放在源文件中,以便编译器可以正确地生成代码。
如果将函数定义放在头文件中,会导致函数在多个源文件中重复定义,引发链接错误。
因此,通常在头文件中只包含函数的声明和类型定义,而函数的具体实现则放在源文件中。
2、实现模块化
头文件的目的是为了实现模块化、代码重用和减少重复编写的目标,通过将常用的函数和变量的声明放在头文件中,可以方便地在多个源文件中引用和共享。同时,头文件也可以提供接口的声明,方便其他开发人员了解和使用代码库或模块。
3、避免多重复定义
头文件中通常只包含声明而不包含实现(具体的代码逻辑),实现应该放在对应的源代码文件中。这样可以避免在多个源文件中重复定义同一个函数或变量,从而引发多重定义的错误。
三、<>和""之间的区别
两者之间是没有多大差别的,只是为了提高查找效率而区分的。
<>:意为标准头文件:
使用<>这种方式,编译器会在编译器安装目录的标准库中开始查找该头文件。
这种方式适用于包含标准库或系统级别的头文件。
例如:
C语言中标准的输入输出头文件是 <stdio.h>。
C++语言中标准的输入输出头文件是< iostream >。
“”:意为自定义头文件:
""这种方式,会在当前的工程所在的文件夹,也就是源文件所在的文件夹中开始寻找。
总之,使用 <> 包裹的文件名会首先在默认的系统库路径下查找,而使用 “” 包裹的文件名会首先在当前源文件目录中查找。
四、#ifndef/#define/
4.1#ifndef/#define/的作用
头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用
其实“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复引用。
为防止重复包含头文件,采用如下三行代码:
#ifndef A_H (ifndef是if not define的简写,其中的A一般是该头文件名称的大写。该行代码用于测试A头文件是否被宏定义过)
#define A_H (如果A没有被宏定义过,则定义A,并接下来进行编写宏定义)
// C语言头文件中的声明
#endif
4.2头文件被重复引用引起的后果
有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言编译效率低下那将是一件多么痛苦的事情。
有些头文件重复包含,会引起错误,比如在头文件中定义了全局变量(虽然这种方式不被推荐,但确实是C规范允许的)这种会引起重复定义。
4.3后面的名字命名要求
在C语言中,#ifndef、#define 后面的名字命名没有严格的要求,但为了避免与其他标识符冲突并提高代码的可读性,通常遵循以下命名约定:
- 使用大写字母:常量和预处理器宏的命名通常使用全大写字母,这是一种常见的约定;并且与文件名相同。例如:
#ifndef MY_HEADER_H
#define MY_HEADER_H
// 定义和声明
#endif
- 使用下划线分隔:如果需要使用多个单词来命名,可以使用下划线
_
来分隔单词,以提高可读性。例如:
#ifndef CONFIG_SETTINGS_H
#define CONFIG_SETTINGS_H
// 定义和声明
#endif
-
避免与关键字和标准库冲突:确保命名不与C语言的关键字和标准库的函数或常量命名冲突。例如,不要使用
int
、char
、printf
等作为命名。 -
使用有意义的名称:命名应该具备清晰的意义,可以通过名称直观地理解其含义和目的。这有助于提高代码的可读性和维护性。
总而言之,虽然没有严格的命名规则,但使用大写字母、下划线分隔、避免冲突以及具有可读性的名称可以提高代码的可读性和清晰性。选择一种合理的命名约定,并在整个代码库中保持一致。
五、头文件程序编写规范
除了程序的入口函数,通常每一个 .c 文件应该有一个相应的同名 .h 文件
1、在 C 语言中,通常情况下每一个 .c 文件会对应一个同名的 .h 文件。这是为了实现模块化和代码组织的目的。头文件(.h 文件)中会包含该模块或代码文件中的函数和变量的声明,供其他源代码文件引用和使用。
同时,与 .h 文件对应的 .c 文件中则包含这些函数和变量的具体实现(定义)。这种分离将函数的声明(接口)和定义(实现)分离开来,可以提高代码的可读性、可维护性和可重用性。
2、然而,并不是每一个 .c 文件都需要一个同名的 .h 文件,这取决于具体的项目和代码组织的方式。有些 .c 文件可能只是包含一些主函数(入口函数),而不涉及额外的函数和变量。这种情况下可能不需要为该 .c 文件编写一个相应的 .h 文件。
3、所以,可以看出这句话并不是绝对的规则,而是一个常见的约定和最佳实践。根据具体的项目需求和代码组织方式,可能会有特殊情况。
六、预处理器指令
#include
C语言中的预处理器指令有以下几种常用的:
1. #include
:用于包含其他文件的内容到当前文件中。可以使用尖括号 <> 或双引号 “” 包含文件名。
#include // 包含标准库的头文件
#include "my_header.h" // 包含自定义头文件
#define
#define
:除了宏常量和宏函数,#define 也经常用于条件编译,根据不同的宏定义包含或排除代码块的定义。例如:
#ifndef UTILS_H
#define UTILS_H// 头文件内容
#endif
这种技术可避免头文件的重复包含,确保头文件内容只被编译一次。
#ifdef
#ifdef
:用于检查指定的宏是否已经定义,若已定义则执行相应的代码块。
#ifdef DEBUG
printf("Debug mode enabled.\n");
#endif
#ifndef
#ifndef
:用于检查指定的宏是否未定义,若未定义则执行相应的代码块。
#ifndef PI
#define PI 3.14159
#endif
#if、#elif、#else、#endif
#if
、#elif
、#else
、#endif
:用于条件编译,根据指定条件选择性地编译代码块。
#if defined(PLATFORM_WINDOWS)
// Windows平台特定代码
#elif defined(PLATFORM_LINUX)
// Linux平台特定代码
#else
// 默认代码块
#endif
#pragma
#pragma
:用于向编译器发送特定的指示。
#pragma warning(disable: 123) // 禁用特定的编译警告
#pragma pack(1) // 设置结构体的内存对齐方式
这些预处理器指令可以在编译之前对源代码进行预处理,对代码进行宏替换、文件包含、条件编译等操作。它们提供了一种在编译时根据不同的条件来选择性地处理代码的灵活机制,用于实现不同平台、不同配置下的代码定制和条件编译。预处理器指令在编译过程之前被执行,不是普通的C语句,也不会转化为目标代码。