有哪些方法来模块化C代码?

时间:2009-07-22 03:10:40

标签: c namespaces module encapsulation scoping

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

8 个答案:

答案 0 :(得分:15)

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

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

答案 1 :(得分:10)

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

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

答案 2 :(得分:3)

High and Low-Level C文章包含很多好的提示。特别是,请查看“类和对象”部分。

Standards and Style for Coding in ANSI C还包含您可以选择的好建议。

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:2)

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

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

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

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

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

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

答案 6 :(得分:1)

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

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

答案 7 :(得分:0)

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