使用各种头文件以及与应用程序文件分开的代码/实现文件,旨在促进代码的可维护性和重复使用。通过将所有相关函数、变量和数据结构收集到单个文件中,我们可以避免在每个后续应用程序中复制该代码。
例如,假设您将开发与矩阵或数组相关的例程。此方案使您能够将处理数组的常见例程收集到单个文件中,例如
arrays.c
。现在,无论您写多少个程序,您都有一个包含数组例程的公共文件。如果您编写了5个需要数组例程的应用程序,您可以在
arrays.c
中随时使用它们。
为了可维护性,假设您现在有一种更有效的方法来处理从文件到数组的输入。您只需更新
arrays.c
中的一个
read_array
函数,而不是在5个不同的应用程序程序文件中返回并更新它,就可以完成更新那些例程以供使用的所有应用程序。
那么
header
文件如何适应呢?为了在其他应用程序中使用您的数组函数,这些应用程序需要一种通用的方式来包含代码以使其可用于使用。这是通过在需要它的每个应用程序中包含您的
arrays.h
头文件来完成的。那么
arrays.h
中放什么,
arrays.c
中放什么呢?您的头文件将包括
header文件、函数声明、数据结构和变量
,这些对于实现
arrays.c
中包含的
function definitions
是必要的。这样,任何希望使用您的数组例程的应用程序只需在其文件开头包含一行
#include "arrays.h"
,即可访问包含在
arrays.c
中的数组函数。
以下是一个示例(使用C语言),以帮助澄清问题。假设您有几个数组函数,可以将文本文件中的行读入字符串
read_array
,打印行
prn_array
,然后释放数组和字符串所使用的内存
free_array
。您已将数组函数收集到
array.c
中。(我们知道我们将在顶部包含
array.h
。)例如:
#include "array.h"
char **read_array (char *filename)
{
char *buffer = NULL;
size_t len = 0;
ssize_t read;
char **array = NULL;
int cnt = 0;
FILE *fp;
fp = fopen (filename, "r");
if (!fp) {
fprintf (stderr, "failed to open file for reading\n");
return NULL;
}
array = calloc (AMAX, sizeof (*array) * AMAX);
while ((read = getline (&buffer, &len, fp)) != -1) {
if (cnt == AMAX) {
fprintf (stderr, "Error: AMAX reached\n");
break;
}
if (buffer[read-1] == '\n') {
buffer[read-1] = 0;
read -= 1;
}
array[cnt] = strdup (buffer);
cnt++;
}
fclose (fp);
return array;
}
void prn_array (char **array)
{
register int j = 0;
while (array[j]) {
printf (" array[%d]: %s\n", j, array[j]);
j++;
}
}
void free_array (char **array)
{
register int j = 0;
while (array[j]) {
free (array[j]);
j++;
}
free (array);
}
现在,让我们创建我们的标题。我们收集我们的函数声明以及使用的任何变量或数据结构,并包括 array.h 中的系统头文件和函数所需的任何其他已包含的头文件。
#ifndef MY_ARRAY_H
#define MY_ARRAY_H 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define AMAX 100
char **read_array (char *filename);
void prn_array (char **array);
void free_array (char **array);
#endif
注意:我们将头文件的内容包含在一个测试
#ifndef MY_ARRAY_H
中,这只是询问编译器是否已经定义了
MY_ARRAY_H
,如果没有定义,则让编译器知道从我们的
#define
开始直到达到if语句结束前的
#endif
的所有内容都被定义为
MY_ARRAY_H
,这样如果另一个文件尝试包含相同的头文件,编译器就知道它已经有了,不需要再次包含它。(不要求,但我们还给出了
MY_ARRAY_H
的定义值
1
)
那么我们如何利用
arrays.c
中的自定义头文件
array.h
和处理数组的自定义函数呢?我们编写一个使用
arrays.c
中的函数的应用程序,并在应用程序中声明
arrays.c
中的所有函数,我们只需在应用程序中包含
array.h
即可。(下面称为
application.c
)
#include <stdio.h>
#include "array.h"
int main (int argc, char *argv[]) {
if (argc < 2) {
printf ("filename.csv please...\n");
return 1;
}
char **my_array = NULL;
my_array = read_array (argv[1]);
prn_array (my_array);
free_array (my_array);
return 0;
}
编译"application.c"和"array.c"文件:
gcc -Wall -Wextra -o application application.c array.c
可以在任何小于100行的文本文件上运行它
$ ./application input.txt
array[0]: 1. This is a simple input file with line numbers
array[1]: 2. for use with application showing the use of
array[2]: 3. header file: array.h
array[3]: 4. array functions defined in: array.c
array[4]: 5. (blank)
array[5]: 6. compiled with the following:
array[6]: 7. gcc -Wall -Wextra -o application application.c array.c
array[7]: 8. --------
array[8]: 9. Now you know!
现在,您可以看到创建一个包含相关函数、变量和数据结构的单独文件以及创建头文件的好处。我们可以通过在应用程序中包含头文件“arrays.h”来在任何应用程序中重复使用该代码。我们可以在一个地方更新和维护所有数组函数,并且更新我们的“arrays.c”将更新所有其他使用该代码的应用程序。
我知道我没有涵盖所有内容,但请确保您对这些概念有所了解。它们是使用C/C++中的多个源文件和包含文件的基本概念。
#include
相当于复制粘贴所包含文件的内容。这有很多影响,但最重要的是它将帮助您理解链接器何时抱怨重复符号,这无疑会遇到。 - hyde