72

这一切都在标题中;我认为超级简单,但是在任何地方都很难搜索语法上的东西。

这是我从CS50.net复制的两个库文件,我想知道为什么它们有两个不同的扩展名。

4

6 回答 6

130

.c : c 文件(一般来说,真正的动作在哪里)

.h :头文件(包含在预处理器#include指令中)。包含通常被认为与代码的其他部分共享的内容,例如函数原型、#define 的内容、全局变量的外部声明(哦,太可怕了)等等。

从技术上讲,您可以将所有内容放在一个文件中。一个完整的 C 程序。万行。但是我们人类倾向于组织事物。因此,您创建了不同的 C 文件,每个文件都包含特定的函数。这一切都很好,很干净。然后你突然意识到你对给定 C 文件的声明应该也存在于另一个 C 文件中。所以你会复制它们。因此,最好的办法是提取声明并将其放入一个通用文件中,即 .h

例如,在 cs50.h 中,您可以找到所谓的函数“前向声明”。前向声明是告诉编译器应该如何调用函数(例如什么输入参数)以及它返回什么的快速方法,因此它可以执行正确的检查(例如,如果您调用具有错误数量的参数的函数,它会抱怨)。

另一个例子。假设您编写了一个 .c 文件,其中包含一个执行正则表达式匹配的函数。您希望您的函数接受正则表达式、要匹配的字符串和一个参数,该参数指示比较是否必须不区分大小写。

因此,您将在 .c 中放置

bool matches(string regexp, string s, int flags) { the code }

现在,假设您要传递以下标志:

0:如果搜索区分大小写

1:如果搜索不区分大小写

而且你想让自己对新标志保持开放,所以你没有放一个布尔值。玩数字很困难,所以你为这些标志定义有用的名字

#define MATCH_CASE_SENSITIVE 0
#define MATCH_CASE_INSENSITIVE 1

此信息进入 .h,因为如果任何程序想要使用这些标签,除非您包含这些信息,否则它无法知道它们。当然,您可以将它们放在 .c 中,但是您必须包含 .c 代码(整个!),这是浪费时间和麻烦的根源。

于 2009-11-08T03:07:12.923 回答
22

当然,并没有说头文件.h的扩展名必须是 C 源文件的扩展名必须是.c. 这些是有用的约定。

E:\Temp> type my.interface
#ifndef MY_INTERFACE_INCLUDED
#define MYBUFFERSIZE 8
#define MY_INTERFACE_INCLUDED
#endif

E:\Temp> type my.source
#include <stdio.h>

#include "my.interface"

int main(void) {
    char x[MYBUFFERSIZE] = {0};
    x[0] = 'a';
    puts(x);
    return 0;
}

E:\Temp> gcc -x c my.source -o my.exe

E:\Temp> my
a
于 2009-11-08T03:59:12.740 回答
6

它们不是真正的库文件。它们只是源文件。就像 Stefano 所说,.c文件是 C 源文件,它实际上使用/定义了它在.h文件中概述的实际源代码,即头文件。头文件通常概述了将在实际源文件中使用的所有函数原型和结构。把它想象成一个参考/附录。这在查看头文件时很明显,正如您将看到的那样:) 因此,当您想使用这些源文件中编写的内容时,您#include就是头文件,其中包含编译器需要知道的信息。

于 2009-11-08T03:08:57.943 回答
3

.c 是源文件,.h 是头文件

于 2009-11-08T03:07:38.273 回答
1

.c 文件是将被编译的源文件。.h 文件用于将程序的 API 公开给该程序的其他部分或您正在创建库的其他程序。

例如,程序 PizzaDelivery 可以有 1 个带有主程序的 .c 文件,以及 1 个带有实用功能的 .c 文件。现在,为了使程序的主要部分能够使用实用函数,您需要通过函数原型将 API 公开到一个 .h 文件中,这个 .h 文件包含在主 .c 文件中。

于 2009-11-08T03:12:19.573 回答
0
.c : 'C' source code
.h : Header file

通常,.c文件包含实现,而.h文件包含实现的“接口”。

于 2009-11-08T03:08:07.820 回答