有什么方法来模块化C代码?

您知道在项目规模扩大时模块化C代码有哪些方法,实践和约定?

创建只包含使用模块所需内容的头文件。 在相应的.c文件中,使外部不可见的任何东西(例如辅助函数)静态化。 在外部可见的所有内容的名称上使用前缀,以帮助避免命名空间冲突。 (如果一个模块跨越多个文件,事情会变得更难。因为你可能需要暴露内部事物而不能用“静态”隐藏它们)

(如果我要尝试改进C,我要做的一件事就是将“静态”作为函数的默认范围。如果你想在外面看到一些东西,你必须用“export”或“global”或其他东西标记它类似。)

OO技术可以应用于C代码,它们只需要更多的规则。

  • 使用不透明手柄操作对象。 这是如何完成的一个很好的例子是stdio库 – 一切都是围绕不透明的FILE*句柄组织的。 围绕这个原则组织了许多成功的库(例如zlib , apr )
  • 因为struct的所有成员在C中都是隐式public的,所以你需要一个约定+程序员规则来强制执行有用的信息隐藏技术。 选择一个简单的,可自动检查的约定,例如“私有成员以’_’结尾”。
  • 可以使用指向函数的指针数组来实现接口 。 当然,这需要比提供语言支持的C ++等语言更多的工作,但它仍然可以用C语言完成。

高级和低级C文章包含许多好的提示。 特别是,看看“ 类和对象 ”部分。

ANSI C中的编码标准和样式也包含您可以选择的好建议。

  1. 不要在头文件中定义变量; 相反,在源文件中定义变量并在标头中添加extern语句(声明)。 这将与#2和#3联系在一起。
  2. 在每个标题上使用包含保护。 这将节省许多令人头疼的问题。
  3. 假设您已完成#1和#2,请为该文件中的某个文件包含您需要的所有内容(但仅限于您需要的内容)。 不要依赖于编译器如何扩展include指令的顺序。

Pidgin(以前的Gaim)使用的方法是创建一个Plugin结构。 每个插件都使用回调来填充结构,以进行初始化和拆卸,以及一堆其他描述性信息。 除结构之外的所有内容都被声明为静态,因此只有Plugin结构才会被公开以进行链接。

然后,为了处理与应用程序的其余部分通信的插件的松散耦合(因为如果它在设置和拆除之间做了某些事情会很好),它们具有信令系统。 当特定信号(不是标准C信号,但是自定义可扩展类型[由字符串而不是设置代码])由应用程序的任何部分(包括另一个插件)发出时,插件可以注册要调用的回调。 他们也可以自己发出信号。

这似乎在实践中运作良好 – 不同的插件可以相互构建,但耦合相当松散 – 没有直接调用函数,一切都通过信号系统。

一个函数应该做一件事,并做好一件事。

较大的包装器函数使用的许多小函数有助于从小的,易于理解(和测试!)构建块构造代码。

创建每个都有几个函数的小模块。 只暴露您必须的东西,在模块内部保持其他任何静态。 将小模块与其.h接口文件链接在一起。

提供Getter和Setter函数,以访问模块中的静态文件范围变量。 这样,变量实际上只写在一个地方。 这还有助于使用函数和调用堆栈中的断点来跟踪对这些静态变量的访问。

设计模块化代码时的一个重要规则是:除非必须,否则不要尝试进行优化。 许多小函数通常会产生更清晰,结构良好的代码,额外的函数调用开销可能是值得的。

我总是试图将变量保持在最窄的范围内,也在函数内。 例如,for循环的索引通常可以保持在块范围内,并且不需要在整个函数级别公开。 C不像C ++那样灵活,“在你使用它的地方定义它”,但它是可行的。

将代码分解为相关函数的库是保持组织有序的一种方法。 为了避免名称冲突,你也可以使用前缀来重复使用函数名称,虽然名字很好但我从来没有真正发现这是一个很大的问题。 例如,如果您想开发自己的数学例程但仍然使用标准数学库中的一些,则可以为您的前缀添加一些字符串:xyz_sin(),xyz_cos()。

通常我更喜欢每个文件的一个函数(或一组密切相关的函数)和每个源文件约定一个头文件。 将文件分成目录,每个目录代表一个单独的库也是一个好主意。 您通常有一个makefile或构建文件系统,允许您在代表各种库/程序的层次结构之后构建整个系统的全部或部分。

有目录和文件,但没有名称空间或封装。 您可以将每个模块编译为单独的obj文件,并将它们链接在一起(作为库)。