C源文件的好处包括它自己的头文件

时间:2021-01-28 15:05:56

I understand that if a source file need to reference functions from other file then it needs to include its header file, but I don't understand why the source file include its own header file. Content in header file is simply being copied and pasted into the source file as function declarations in per-processing time. For source file who include its own header file, such "declaration" doesn't seem necessary to me, in fact, project still compile and link no problem after remove the header from it's source file, so what's the reason for source file include its own header?

据我所知,如果源文件需要引用其他文件中的函数,那么它需要包含其头文件,但我不明白为什么源文件包含自己的头文件。头文件中的内容只是作为每个处理时间的函数声明被复制并粘贴到源文件中。对于包含自己的头文件的源文件,这样的“声明”对我来说似乎没有必要,实际上,项目在从源文件中删除标题后仍然编译并链接没有问题,所以源文件的原因是什么包括它自己的头?

5 个解决方案

#1


15  

The main benefit is having the compiler verify consistency of your header and its implementation. You do it because it is convenient, not because it is required. It may definitely be possible to get the project to compile and run correctly without such inclusion, but it complicates maintenance of your project in the long run.

主要好处是让编译器验证标头及其实现的一致性。你这样做是因为方便,不是因为它是必需的。绝对有可能让项目在没有这种包含的情况下正确编译和运行,但从长远来看,它会使项目的维护变得复杂。

If your file does not include its own header, you can accidentally get in a situation when forward declaration of a function does not match the definition of the function - perhaps because you added or removed a parameter, and forgot to update the header. When this happens, the code relying on the function with mismatch would still compile, but the call would result in undefined behavior. It is much better to have the compiler catch this error, which happens automatically when your source file includes its own header.

如果你的文件没有自己的标题,你可能会意外地遇到函数的前向声明与函数定义不匹配的情况 - 可能是因为你添加或删除了一个参数,并忘了更新标题。发生这种情况时,依赖于不匹配函数的代码仍然会编译,但调用会导致未定义的行为。让编译器捕获此错误要好得多,这在源文件包含自己的头文件时会自动发生。

#2


4  

The header file tells people what the source file can do.

头文件告诉人们源文件可以做什么。

So the source file for the header file needs to know its obligations. That is why it is included.

因此头文件的源文件需要知道它的义务。这就是它被包括在内的原因。

#3


4  

Yours seems a borderline case, but an include file can be viewed as a sort of contract between that source file and any other source files that may require those functions.

您的似乎是一个边缘情况,但是包含文件可以被视为该源文件与可能需要这些功能的任何其他源文件之间的一种契约。

By writing the "contract" in a header file, you can ensure that the other source files will know how to invoke those functions, or, rather, you will be sure that the compiler will insert the correct code and check its validity at compile time.

通过在头文件中编写“契约”,您可以确保其他源文件知道如何调用这些函数,或者更确切地说,您将确保编译器将插入正确的代码并在编译时检查其有效性。

But what if you then (even inadvertently) changed the function prototype in the corresponding source file?

但是,如果您(甚至无意中)更改了相应源文件中的函数原型,该怎么办?

By including in that file the same header as everyone else, you will be warned at compile time should a change inadvertently "break" the contract.

通过在该文件中包含与其他人相同的标题,如果更改无意中“破坏”合同,您将在编译时被警告。

Update (from @tmlen's comment): even if in this case it does not, an include file may also use declarations and pragmas such as #defines, typedef, enum, struct and inline as well as compiler macros, that would make no sense writing more than once (actually that would be dangerous to write in two different places, lest the copies get out of sync with each other with disastrous results). Some of those (e.g. a structure padding pragma) could become bugs difficult to track down.

更新(来自@tmlen的评论):即使在这种情况下它没有,包含文件也可能使用声明和编译指示,如#define,typedef,enum,struct和inline以及编译器宏,这些都没有意义不止一次(实际上,在两个不同的地方写作是危险的,以免副本彼此不同步而带来灾难性后果)。其中一些(例如结构填充编译指示)可能成为难以追踪的错误。

#4


0  

It is useful because functions can be declared before they are defined.

它很有用,因为函数可以在定义之前声明。

So it happens that you have the declaration, followed by a call\invocation, followed by the implementation. You don't have to, but you can.

所以碰巧你有声明,然后是call \ _调用,然后是实现。你不必,但你可以。

The header file contains the declarations. You're free to invoke anytime as long as the prototype matches. And as long as the compiler finds an implementation before finishing compilation.

头文件包含声明。只要原型匹配,您就可以随时调用。只要编译器在完成编译之前找到实现。

#5


0  

Practical example - assume the following files in a project:

实际示例 - 假设项目中包含以下文件:

/* foo.h */
#ifndef FOO_H
#define FOO_H
double foo( int x );
#endif

/* foo.c */
int foo( int x )
{
  ...
}

/* main.c */
#include "foo.h"

int main( void )
{
  double x = foo( 1 );
  ...
}

Note that the declaration infoo.h does not match the definition in foo.c; the return types are different. main.c calls the foo function assuming it returns a double, according to the declaration in foo.h.

请注意,声明infoo.h与foo.c中的定义不匹配;返回类型不同。根据foo.h中的声明,main.c调用foo函数,假设它返回一个double。

foo.c and main.c are compiled separately from each other. Since main.c calls foo as declared in foo.h, it compiles successfully. Since foo.c does not include foo.h, the compiler is not aware of the type mismatch between the declaration and definition, so it compiles successfully as well.

foo.c和main.c是彼此分开编译的。由于main.c调用foo.h中声明的foo,因此编译成功。由于foo.c不包含foo.h,编译器不知道声明和定义之间的类型不匹配,因此它也能成功编译。

When you link the two object files together, the machine code for the function call won't match up with what the machine code for function definition expects. The function call expects a double value to be returned, but the function definition returns an int. This is a problem, especially if the two types aren't the same size. Best case scenario is you get a garbage result.

将两个目标文件链接在一起时,函数调用的机器代码将与函数定义所需的机器代码不匹配。函数调用期望返回double值,但函数定义返回int。这是一个问题,特别是如果这两种类型的大小不同。最好的情况是你得到一个垃圾结果。

By including foo.h in foo.c, the compiler can catch this mismatch before you run your program.

通过在foo.c中包含foo.h,编译器可以在运行程序之前捕获这种不匹配。

And, as is pointed out in an earlier answer, if foo.h defines any types or constants used by foo.c, then you definitely need to include it.

并且,正如在前面的回答中指出的,如果foo.h定义了foo.c使用的任何类型或常量,那么你肯定需要包含它。

#1


15  

The main benefit is having the compiler verify consistency of your header and its implementation. You do it because it is convenient, not because it is required. It may definitely be possible to get the project to compile and run correctly without such inclusion, but it complicates maintenance of your project in the long run.

主要好处是让编译器验证标头及其实现的一致性。你这样做是因为方便,不是因为它是必需的。绝对有可能让项目在没有这种包含的情况下正确编译和运行,但从长远来看,它会使项目的维护变得复杂。

If your file does not include its own header, you can accidentally get in a situation when forward declaration of a function does not match the definition of the function - perhaps because you added or removed a parameter, and forgot to update the header. When this happens, the code relying on the function with mismatch would still compile, but the call would result in undefined behavior. It is much better to have the compiler catch this error, which happens automatically when your source file includes its own header.

如果你的文件没有自己的标题,你可能会意外地遇到函数的前向声明与函数定义不匹配的情况 - 可能是因为你添加或删除了一个参数,并忘了更新标题。发生这种情况时,依赖于不匹配函数的代码仍然会编译,但调用会导致未定义的行为。让编译器捕获此错误要好得多,这在源文件包含自己的头文件时会自动发生。

#2


4  

The header file tells people what the source file can do.

头文件告诉人们源文件可以做什么。

So the source file for the header file needs to know its obligations. That is why it is included.

因此头文件的源文件需要知道它的义务。这就是它被包括在内的原因。

#3


4  

Yours seems a borderline case, but an include file can be viewed as a sort of contract between that source file and any other source files that may require those functions.

您的似乎是一个边缘情况,但是包含文件可以被视为该源文件与可能需要这些功能的任何其他源文件之间的一种契约。

By writing the "contract" in a header file, you can ensure that the other source files will know how to invoke those functions, or, rather, you will be sure that the compiler will insert the correct code and check its validity at compile time.

通过在头文件中编写“契约”,您可以确保其他源文件知道如何调用这些函数,或者更确切地说,您将确保编译器将插入正确的代码并在编译时检查其有效性。

But what if you then (even inadvertently) changed the function prototype in the corresponding source file?

但是,如果您(甚至无意中)更改了相应源文件中的函数原型,该怎么办?

By including in that file the same header as everyone else, you will be warned at compile time should a change inadvertently "break" the contract.

通过在该文件中包含与其他人相同的标题,如果更改无意中“破坏”合同,您将在编译时被警告。

Update (from @tmlen's comment): even if in this case it does not, an include file may also use declarations and pragmas such as #defines, typedef, enum, struct and inline as well as compiler macros, that would make no sense writing more than once (actually that would be dangerous to write in two different places, lest the copies get out of sync with each other with disastrous results). Some of those (e.g. a structure padding pragma) could become bugs difficult to track down.

更新(来自@tmlen的评论):即使在这种情况下它没有,包含文件也可能使用声明和编译指示,如#define,typedef,enum,struct和inline以及编译器宏,这些都没有意义不止一次(实际上,在两个不同的地方写作是危险的,以免副本彼此不同步而带来灾难性后果)。其中一些(例如结构填充编译指示)可能成为难以追踪的错误。

#4


0  

It is useful because functions can be declared before they are defined.

它很有用,因为函数可以在定义之前声明。

So it happens that you have the declaration, followed by a call\invocation, followed by the implementation. You don't have to, but you can.

所以碰巧你有声明,然后是call \ _调用,然后是实现。你不必,但你可以。

The header file contains the declarations. You're free to invoke anytime as long as the prototype matches. And as long as the compiler finds an implementation before finishing compilation.

头文件包含声明。只要原型匹配,您就可以随时调用。只要编译器在完成编译之前找到实现。

#5


0  

Practical example - assume the following files in a project:

实际示例 - 假设项目中包含以下文件:

/* foo.h */
#ifndef FOO_H
#define FOO_H
double foo( int x );
#endif

/* foo.c */
int foo( int x )
{
  ...
}

/* main.c */
#include "foo.h"

int main( void )
{
  double x = foo( 1 );
  ...
}

Note that the declaration infoo.h does not match the definition in foo.c; the return types are different. main.c calls the foo function assuming it returns a double, according to the declaration in foo.h.

请注意,声明infoo.h与foo.c中的定义不匹配;返回类型不同。根据foo.h中的声明,main.c调用foo函数,假设它返回一个double。

foo.c and main.c are compiled separately from each other. Since main.c calls foo as declared in foo.h, it compiles successfully. Since foo.c does not include foo.h, the compiler is not aware of the type mismatch between the declaration and definition, so it compiles successfully as well.

foo.c和main.c是彼此分开编译的。由于main.c调用foo.h中声明的foo,因此编译成功。由于foo.c不包含foo.h,编译器不知道声明和定义之间的类型不匹配,因此它也能成功编译。

When you link the two object files together, the machine code for the function call won't match up with what the machine code for function definition expects. The function call expects a double value to be returned, but the function definition returns an int. This is a problem, especially if the two types aren't the same size. Best case scenario is you get a garbage result.

将两个目标文件链接在一起时,函数调用的机器代码将与函数定义所需的机器代码不匹配。函数调用期望返回double值,但函数定义返回int。这是一个问题,特别是如果这两种类型的大小不同。最好的情况是你得到一个垃圾结果。

By including foo.h in foo.c, the compiler can catch this mismatch before you run your program.

通过在foo.c中包含foo.h,编译器可以在运行程序之前捕获这种不匹配。

And, as is pointed out in an earlier answer, if foo.h defines any types or constants used by foo.c, then you definitely need to include it.

并且,正如在前面的回答中指出的,如果foo.h定义了foo.c使用的任何类型或常量,那么你肯定需要包含它。