C语言中的设计问题

C语言中的设计问题,c,struct,header,C,Struct,Header,我正在努力解决一个设计问题,我正在努力找到适合我的情况的“最佳实践”答案 假设我有一个名为Logger.c(和Logger.h)的文件,负责记录程序中的操作 我希望我的所有模块都引用记录器,这样每个模块都有一个 #包括Logger.h (Inside Logger.h) #include NTFS_Partition VOID Log_Partition(NTFS_Partition *part); // logger.h #include "structs.h" VOID Log_Part

我正在努力解决一个设计问题,我正在努力找到适合我的情况的“最佳实践”答案

假设我有一个名为
Logger.c
(和
Logger.h
)的文件,负责记录程序中的操作

我希望我的所有模块都引用记录器,这样每个模块都有一个
#包括Logger.h

(Inside Logger.h)

#include NTFS_Partition
VOID Log_Partition(NTFS_Partition *part);
// logger.h
#include "structs.h"
VOID Log_Partition(NTFS_Partition *part);
VOID Log_Partition(FAT32_Partition *part);
VOID Log_Partition(FAT16_Partition *part);
假设我有一个名为
NTFS.c
的模块,负责与NTFS的交互,这个模块有在其头文件中定义的特殊结构,例如:
NTFS\u分区。

问题是:

一方面,我希望logger能够将NTFS#U分区的格式化表示打印到日志文件中,这样我就必须将NTFS.h包含在
logger.h

(Inside Logger.h)

#include NTFS_Partition
VOID Log_Partition(NTFS_Partition *part);
// logger.h
#include "structs.h"
VOID Log_Partition(NTFS_Partition *part);
VOID Log_Partition(FAT32_Partition *part);
VOID Log_Partition(FAT16_Partition *part);
另一方面,我不确定记录器是否应该重新引用引用他的模块

目前,我看到了两个主要的选择:

1.Logger.h包括NTFS.h,NTFS.c包括Logger.h(这是可行的)

2.我创建了一个名为NTFS_Types.h的新头文件,该文件将在所有服务器上共享 模块,并且只包含NTFS结构的减速(如NTFS_分区)

非常感谢,
Michael。

您可以创建一个共享头,其中定义了所有结构

// structs.h
struct NTFS_Partition { .. };
struct FAT32_Partition { .. };
struct FAT16_Partition { .. };
将其包含在
logger.h

(Inside Logger.h)

#include NTFS_Partition
VOID Log_Partition(NTFS_Partition *part);
// logger.h
#include "structs.h"
VOID Log_Partition(NTFS_Partition *part);
VOID Log_Partition(FAT32_Partition *part);
VOID Log_Partition(FAT16_Partition *part);
并在各种源文件中包含
logger.h

// NTFS.c
#include "logger.h"
// FAT32.c
#include "logger.h"
// FAT16.c
#include "logger.h"

在C++中,最好在不同的头文件中保留不同的不相关类定义。但在C中,将不同的结构定义放置在单独的报头中可能是一个过份的错误。

< P>,在C或C++中是否编码并不完全清楚;我将假设C(因此没有重载函数名等)。在我看来,您需要“向前声明”您的结构。在
Logger.h
中,您可以编写:

#ifndef LOGGER_H_INCLUDED
#define LOGGER_H_INCLUDED

struct NTFS_Partition;     // No details - just the name (3 times)
struct FAT16_Partition;
struct FAT32_Partition;

...

void Log_NTFS_Partition(struct NTFS_Partition *part);
void Log_FAT16_Partition(struct FAT16_Partition *part);
void Log_FAT32_Partition(struct FAT32_Partition *part);

#endif // LOGGER_H_INCLUDED
typedef struct NTFS_Partition NTFS_Partition;
这是普通客户端(属于
Logger.h
)需要知道的所有信息

如果特定客户机正在处理NTFS分区,那么它不仅包括
Logger.h
,还包括
NTFS.h
,这将提供
struct NTFS_Partition{…}的完整定义,因此客户端可以创建结构实例并用数据填充它。实现日志记录的代码,
Logger.c
,当然还包括
Logger.h
NTFS.h
(和
FAT16.h
FAT32.h
),以便它也可以引用结构的成员

服务的标题(如
Logger.h
)应提供服务客户端编译所需的最少信息量。实现文件可能需要更多信息,但可以从提供它的头中收集额外信息

使用
struct标记
的一个优点就是,它可以根据需要经常重复,而不会弄乱任何东西。如果没有C11,则无法重复
typedef
,因此如果您编写:

#ifndef LOGGER_H_INCLUDED
#define LOGGER_H_INCLUDED

struct NTFS_Partition;     // No details - just the name (3 times)
struct FAT16_Partition;
struct FAT32_Partition;

...

void Log_NTFS_Partition(struct NTFS_Partition *part);
void Log_FAT16_Partition(struct FAT16_Partition *part);
void Log_FAT32_Partition(struct FAT32_Partition *part);

#endif // LOGGER_H_INCLUDED
typedef struct NTFS_Partition NTFS_Partition;
您只能包含该行一次。困难在于确保它只定义一次。为此,您可能会使用诸如
FSTypes.h
之类的头来定义文件系统
typedefs
,该文件系统受到头保护,并包含在需要任何
typedefs
的任何文件中。然后,您可以在不使用前面的
struct
关键字的情况下引用这些类型

如果C++中有代码,则不需要<代码> TyPulf< /Cord>;代码>结构NTFS_分区

声明存在这样的结构类型,并且还声明
NTFS\u分区
作为该类型的名称。如果您的代码是双语的,请使用
typedef
版本;它在C和C++中都有作用。
请注意,如果像
Log\u NTFS\u Partition()
这样的函数采用实际的结构而不是指向结构的指针,则必须在作用域中定义结构。但是,如果函数只接受指针,那么向前声明就足够了。

我更喜欢选项2,因为我不喜欢这样隐藏的循环模块依赖项。如果
Logger
实际上决定在
NTFS.h
中调用API,您可能会遇到一些微妙的实际问题。非常感谢您的评论。请参阅下面我对选项2的评论。你知道这种情况下的更多选择吗?你是用C还是C++编码?另外,您包含的文件名周围的双引号(或尖括号)发生了什么变化?我正在用C语言编写代码(我在标题中写的),非常感谢您的回答。这个选项唯一让我感到“不舒服”的地方是,我不止一次听说头文件应该包含编译和链接模块所需的所有数据。通过将NTFS_分区从NTFS.h中删除,我迫使未来的程序员包括“NTFS.h”和“Structs.h”,以便正确编译和链接。我知道这是一种权衡,但我很高兴知道是否有更好的选择。@MichaelEngstler:让
NTFS.h
include
Structs.h
。这正是我想要的。我还有最后两个问题:模块(.c文件)的实现是循环引用,这是一种最佳实践吗?在您的示例中,NTFS.c将包括Logger.h,Logger.c将包括NTFS.h。我的第二个问题:您是否会创建一个名为FS_Types的新头文件来保存所有结构删除?或者您会将每个strct保留在其.h文件中(例如:struct NTFS_分区将保留在NTFS.h中)Q1:您可以避免循环引用。当你做不到的时候,而且看起来这里会很难,你会尽可能地中和他们。另一种选择是,NTFS.h声明NTFS分区的log函数,但Logger.h不是Logger.c提供的服务的完整描述。我不认为这是一个大问题,但要注意事情。如果它变得普遍,那么你就失去了你的模数