为什么头文件保证了C程序的模块化?
Why do header files ensure C programs' modularity?
当 C 书籍和论文谈论公开接口(.h 文件)和隐藏 implementation.e.g 时,我感到困惑,当 C 主程序想要访问队列等数据结构时,它将包括 queue.h
到它的来源。然后他们会谈论 queue.c
被隐藏在主程序中,即主程序无法访问具体的队列类型。
现在的问题是:实现是什么意思对主程序隐藏并且只能通过头文件定义的接口访问它
main program
------------
//call a function from .h file
Afunction();
function.h
----------
// prototypes
void Afunction(void)
function.c
---------
// concrete implementation
void Afunction(void) {
// your statements here..
{
我正在寻找甚至跨越编译器设计的答案,如果这将清除 me.Thanks
的问题
我们在编程、一般工程以及可能在任何人类努力中学到的重要事情之一是,如果先将问题拆分为更小的问题,然后再解决,那么解决问题会更容易这些问题中的每一个。毕竟一尺高十次,总比一尺高十尺容易。
但要让这种模块化按预期工作,那些较小的问题需要彼此独立,这样您就可以真正解决每个问题,而不必同时考虑所有其他问题。如果你要设计一辆车,把它拆分成引擎、变速器和转向,你希望能够尽可能地设计变速器而不关心转向,而设计转向而不考虑变速器。否则,还是一个大问题,而不是几个小问题。可以说,您如何实现传输的细节必须对转向隐藏。
作为 C 语言的例子,想一想 printf
函数,您可能已经从程序中多次调用它。您 #include
头文件 stdio.h
,其中包含 printf
的 声明 ,看起来像这样:
extern int printf (const char *format, ...);
但是 printf
的 定义 在作为 C 标准库一部分的 .c
文件中。该定义包含解释格式说明符(例如 %10.4f
)并将内容打印到标准输出的实际代码。它可能会或可能不会以源代码形式出现在您的计算机上,但只要您的程序调用 printf
.
,就会使用编译后的形式
现在,您是否曾担心 printf
的内部细节?可能不会,这是一件好事。它对您隐藏,不是因为它是秘密或受版权保护,而是因为您不必担心它。
C 在隐藏实现细节方面做得不好。除非您准备仅将它们导出为指针,否则您无法隐藏数据类型的结构。尽管如此,这仍然可以走很长一段路。
考虑标准 FILE
数据类型。 FILE
的实现需要很多细节,包括操作系统将对象与实际数据流相关联的任何要求、当前缓冲区和缓冲区位置、EOF 和错误标志,以及一堆其他我现在想不起来的事情,因为我不需要知道。
虽然我每天都使用 FILE
s,但我不需要考虑这些事情。我只是按照I/O库提供的方式使用FILE*
,不用担心细节是如何实现的。即使我想,我也不能拿出活动扳手修改内部数据成员。这很好,因为如果我尝试的话,我几乎肯定会弄错。
将程序划分为单独的编译单元也意味着我不需要了解 I/O 库用于实现其外部 API 的内部函数。毫无疑问,它使用了大量的内部函数,每个函数都有一个名字。如果这些名称对我的代码可见,我将必须确保我没有无意中将它们用于我自己的目的。幸运的是,我(大部分)不必担心这个,因为名字(大部分)是隐藏的。
当 C 书籍和论文谈论公开接口(.h 文件)和隐藏 implementation.e.g 时,我感到困惑,当 C 主程序想要访问队列等数据结构时,它将包括 queue.h
到它的来源。然后他们会谈论 queue.c
被隐藏在主程序中,即主程序无法访问具体的队列类型。
现在的问题是:实现是什么意思对主程序隐藏并且只能通过头文件定义的接口访问它
main program
------------
//call a function from .h file
Afunction();
function.h
----------
// prototypes
void Afunction(void)
function.c
---------
// concrete implementation
void Afunction(void) {
// your statements here..
{
我正在寻找甚至跨越编译器设计的答案,如果这将清除 me.Thanks
的问题我们在编程、一般工程以及可能在任何人类努力中学到的重要事情之一是,如果先将问题拆分为更小的问题,然后再解决,那么解决问题会更容易这些问题中的每一个。毕竟一尺高十次,总比一尺高十尺容易。
但要让这种模块化按预期工作,那些较小的问题需要彼此独立,这样您就可以真正解决每个问题,而不必同时考虑所有其他问题。如果你要设计一辆车,把它拆分成引擎、变速器和转向,你希望能够尽可能地设计变速器而不关心转向,而设计转向而不考虑变速器。否则,还是一个大问题,而不是几个小问题。可以说,您如何实现传输的细节必须对转向隐藏。
作为 C 语言的例子,想一想 printf
函数,您可能已经从程序中多次调用它。您 #include
头文件 stdio.h
,其中包含 printf
的 声明 ,看起来像这样:
extern int printf (const char *format, ...);
但是 printf
的 定义 在作为 C 标准库一部分的 .c
文件中。该定义包含解释格式说明符(例如 %10.4f
)并将内容打印到标准输出的实际代码。它可能会或可能不会以源代码形式出现在您的计算机上,但只要您的程序调用 printf
.
现在,您是否曾担心 printf
的内部细节?可能不会,这是一件好事。它对您隐藏,不是因为它是秘密或受版权保护,而是因为您不必担心它。
C 在隐藏实现细节方面做得不好。除非您准备仅将它们导出为指针,否则您无法隐藏数据类型的结构。尽管如此,这仍然可以走很长一段路。
考虑标准 FILE
数据类型。 FILE
的实现需要很多细节,包括操作系统将对象与实际数据流相关联的任何要求、当前缓冲区和缓冲区位置、EOF 和错误标志,以及一堆其他我现在想不起来的事情,因为我不需要知道。
虽然我每天都使用 FILE
s,但我不需要考虑这些事情。我只是按照I/O库提供的方式使用FILE*
,不用担心细节是如何实现的。即使我想,我也不能拿出活动扳手修改内部数据成员。这很好,因为如果我尝试的话,我几乎肯定会弄错。
将程序划分为单独的编译单元也意味着我不需要了解 I/O 库用于实现其外部 API 的内部函数。毫无疑问,它使用了大量的内部函数,每个函数都有一个名字。如果这些名称对我的代码可见,我将必须确保我没有无意中将它们用于我自己的目的。幸运的是,我(大部分)不必担心这个,因为名字(大部分)是隐藏的。