Skip to content

C 头文件

C 语言中的头文件(通常带有 .h 扩展名)包含声明和宏定义,旨在在多个源文件(.c 文件)之间共享。它们充当接口,定义了哪些函数、变量、类型和宏是可用的,而无需提供它们的实现(定义)。

头文件主要有两种类型:

  • 系统头文件(System Header Files): 由 C 编译器和标准库提供(例如,<stdio.h>、<stdlib.h>、<string.h>、<math.h>)。它们声明标准函数、类型和宏。
  • 用户定义头文件(User-Defined Header Files): 由程序员为自己的项目创建,用于组织代码并在他们的 .c 文件之间共享声明(例如,"my_graphics.h"、"network_utils.h")。
  • 模块化(Modularity): 将大型程序分解为更小、更易于管理的 .c 文件(模块)。头文件定义了每个模块的公共接口。
  • 可重用性(Reusability): 在头文件中一次编写的声明可以通过简单地包含头文件在多个 .c 文件中使用。
  • 一致性(Consistency): 确保所有源文件对共享函数、类型和常量使用相同的声明,从而防止错误。
  • 可读性(Readability): 将接口(声明)与实现(定义)分离,使代码更易于理解。
  • 减少编译时间(通过正确使用): 对 .c 文件中实现细节的更改只需要重新编译该文件,而无需重新编译所有使用其接口(在头文件中定义)的文件。

使用 #include 预处理器指令将头文件的内容插入到您的源文件中。

有两种形式:

用于系统头文件。预处理器在标准系统目录列表(编译器特定,通常可配置)中搜索文件。

#include <stdio.h> // Standard Input/Output
// 标准输入/输出
#include <math.h> // Mathematical functions
// 数学函数

用于用户定义头文件(您自己的项目的头文件)。预处理器通常首先在当前源文件的同一目录中搜索文件,然后才在标准系统目录(或指定的其他包含路径)中搜索。

#include "my_module.h"
#include "config/settings.h"

头文件通常应包含声明(declarations),而不是分配存储或生成代码的定义(definitions)。

  • 函数原型(Function Prototypes / Declarations): extern int calculate_sum(int a, int b);
  • 结构体、联合体和枚举定义(Structure, Union, and Enum Definitions): struct Point { int x, y; };,typedef enum { RED, GREEN, BLUE } Color;
  • Typedef 定义: typedef unsigned char BYTE;
  • 宏定义(Macro Definitions): #define MAX_SIZE 100
  • 全局变量声明(使用 extern): extern int global_error_code;(实际定义 int global_error_code = 0; 放在 .c 文件中)。
  • 函数定义(实现): 将函数体放在头文件中会导致多重定义错误(multiple definition errors),如果该头文件被包含在多个 .c 文件中。
  • 全局变量定义(不带 static): 在头文件中定义 int my_global; 会导致多重定义。
  • 大量代码: 头文件应该是简洁的接口。

例外:static inline 函数(C99)通常在头文件中定义,因为它们不会导致链接错误(linker errors)。

如果一个头文件在同一个编译单元(compilation unit)中被多次包含(例如,通过其他头文件间接包含),其内容将被多次处理。这可能导致类型或宏的重定义等错误。

为了防止这种情况,可以使用头文件保护符(include guards),这是一种标准的预处理器习惯用法:

// --- my_module.h ---
#ifndef MY_MODULE_H // Check if MY_MODULE_H is not yet defined
// 检查是否尚未定义 MY_MODULE_H
#define MY_MODULE_H // Define MY_MODULE_H to prevent future inclusions
// 定义 MY_MODULE_H 以防止将来再次包含
// --- Header content goes here ---
// --- 头文件内容在此 ---
#define VERSION "1.0"
struct MyData {
int id;
char value[50];
};
typedef struct MyData DataItem;
extern void process_data(DataItem *item);
// --- End of header content ---
// --- 头文件内容结束 ---
#endif // MY_MODULE_H

第一次包含 my_module.h 时,MY_MODULE_H 未定义,因此预处理器会定义它并处理头文件内容。在同一个 .c 文件中后续包含该头文件时,会发现 MY_MODULE_H 已经定义,预处理器将跳过 #ifndef 和 #endif 之间的内容。

保护宏的名称(MY_MODULE_H)应该对该头文件来说是唯一的(通常基于文件名)。

一些编译器支持 #pragma once 作为更简单的替代方法,但头文件保护符更具可移植性。