C 抽象数据类型指针 - 程序模块化

C Abstract data type pointer - Program modularity

如果某个模块依赖于其他某个子模块,要么将其包含在其头文件中,要么将其包含在其代码文件中,并且如果模块除了对主程序有贡献外还相互依赖怎么办?

下图示意性说明了module1.h/.c需要子模块module4.h,module2需要module3.

的情况

每个头文件都有自己的 typedef 结构示例:

typedef struct list * List;

与头文件相关的源文件实现结构如下:

struct list {
    unsigned length;
    char * value;
};

我在头文件中得到了另一个结构:

typedef enum {START, END, MIDDLE, COMMENTS, CONDITIONS} TypeListBal;
typedef struct bal * Bal;

和源文件:

struct bal {
    TypeListBal type;
    List name;              // Name of the bal
    List attributes[];          // Array of type struct List
};

今晚看了很多书,有一件事不太确定。如果我只是将头文件包含在我需要的源文件中,我将 list.h 包含在 bal.c 中,因为我的 bal.c 得到了一个结构定义,它是输入列表。

要让它工作,我是否必须在我的所有模块中放置特定的#INCLUDE,或者该工作是否会在编译时完成?

我的意思是在 make makefile 中,要有我的 bal.o 对象,它看起来像:

bal.o: list.o bal.c bal.h
       $(CC)   -g -W -Wall -c  list.o bal.c bal.h

因此,只要 makefile 尝试编译 bal.o,编译器就会看到所有依赖项,并且能够编译所有内容。

这就是我的理解方式,但是当我尝试这样做时,我得到:

error: dereferencing pointer to incomplete type

我猜这是因为在我的 bal.c 文件中,我声明了 struct List 类型的变量,而编译器对 struct List 没有任何想法,因为 struct list 的定义在list.c 文件。

所以问题又来了:我如何连接一切以使其正常工作?

如果编译器只看到:

typedef struct list * List;

在包含文件中,并且在其他任何地方都没有定义 struct list,当编译器编译特定的 *.c 文件时,如果 *.c 文件中的代码取消引用 List访问List结构成员的指针,会报错,因为struct list还没有定义

仅仅因为 struct list 的定义存在于其他文件中,同一目录中的其他地方,单独编译,并不意味着编译器在编译时会知道它是什么 这个文件。

单个 *.c 文件的编译与任何其他 *.c 文件的编译是一个完全独立的过程,编译器只会看到明确定义的结构、函数和其他内容 #included,或者在编译那个特定文件时直接在.c文件中声明。

您的 header 文件已损坏。这是一个不完整的类型:

typedef struct bal * Bal;

它需要 struct 声明才能完成。那也应该在 header 文件中。如果在 C 代码中你说 *Bal 没有完整的类型,编译器会放弃,因为它没有关于这个表达式的含义的任何信息,即结构的字段。

您显示的依赖关系图应该绝对没有问题。你想使用守卫来确保 header 文件只包含一次。然后你可以从另一个中包含一个 header,只要没有循环依赖,C 预处理器就会 "just work"。在您的情况下, header 例如模块 4 看起来像:

// module4.h
#ifndef MODULE_4_H_INCLUDED
#define MODULE_4_H_INCLUDED

#include "module1.h"

// all public type declarations for module 4 types

#endif // MODULE_4_H_INCLUDED

使所有 header(包括 module1.h)相似,具有自己的守卫定义。然后模块4代码:

// module4.c
#include "module4.h"

// Use module 4 types.

一般来说,如果您需要某个 .c 文件中的 header 文件给出的类型,请包含它,即使它会被 header 之间的依赖关系包含在内。门卫一般都会保证不冗余。

另外,你的make dependencies的想法是不正确的。例如。要成功编译 module4.c 以获得 module4.o,您(当然)需要 module4.cmodule4.hmodule1.h。这些是构建依赖项。所以规则将是:

module4.o : module4.c module4.h module1.h
        $(CC) -g -W -Wall -c module4.c

但是你不应该像这样写具体的规则。 Make 有 built-ins 和更简单易用的通用规则。并且您可以使用 gcc 本身来生成 header 文件依赖项,而不是手动跟踪它们(这可能导致各种灾难)。参见 the -M option