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 文件的编译是一个完全独立的过程,编译器只会看到明确定义的结构、函数和其他内容 #include
d,或者在编译那个特定文件时直接在.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.c
、module4.h
和 module1.h
。这些是构建依赖项。所以规则将是:
module4.o : module4.c module4.h module1.h
$(CC) -g -W -Wall -c module4.c
但是你不应该像这样写具体的规则。 Make 有 built-ins 和更简单易用的通用规则。并且您可以使用 gcc
本身来生成 header 文件依赖项,而不是手动跟踪它们(这可能导致各种灾难)。参见 the -M option。
如果某个模块依赖于其他某个子模块,要么将其包含在其头文件中,要么将其包含在其代码文件中,并且如果模块除了对主程序有贡献外还相互依赖怎么办?
下图示意性说明了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 文件的编译是一个完全独立的过程,编译器只会看到明确定义的结构、函数和其他内容 #include
d,或者在编译那个特定文件时直接在.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.c
、module4.h
和 module1.h
。这些是构建依赖项。所以规则将是:
module4.o : module4.c module4.h module1.h
$(CC) -g -W -Wall -c module4.c
但是你不应该像这样写具体的规则。 Make 有 built-ins 和更简单易用的通用规则。并且您可以使用 gcc
本身来生成 header 文件依赖项,而不是手动跟踪它们(这可能导致各种灾难)。参见 the -M option。