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

发布于 2024-07-27 04:45:02 字数 41 浏览 12 评论 0原文

随着项目规模的扩大,您知道哪些方法、实践和约定可以模块化 C 代码?

What methods, practices and conventions do you know of to modularize C code as a project grows in size?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

小草泠泠 2024-08-03 04:45:02

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

(如果我要尝试改进C,我会做的一件事就是使“ static”函数的默认作用域。如果您想要外部可见的内容,则必须使用“export”或“global”或类似的内容来标记它。)

Create header files which contain ONLY what is necessary to use a module. In the corresponding .c file(s), make anything not meant to be visible outside (e.g. helper functions) static. Use prefixes on the names of everything externally visible to help avoid namespace collisions. (If a module spans multiple files, things become harder., as you may need to expose internal things and not be able hide them with "static")

(If I were to try to improve C, one thing I would do is make "static" the default scoping of functions. If you wanted something visible outside, you'd have to mark it with "export" or "global" or something similar.)

Hello爱情风 2024-08-03 04:45:02

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

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

OO techniques can be applied to C code, they just require more discipline.

  • Use opaque handles to operate on objects. One good example of how this is done is the stdio library -- everything is organised around the opaque FILE* handle. Many successful libraries are organised around this principle (e.g. zlib, apr)
  • Because all members of structs are implicitly public in C, you need a convention + programmer discipline to enforce the useful technique of information hiding. Pick a simple, automatically checkable convention such as "private members end with '_'".
  • Interfaces can be implemented using arrays of pointers to functions. Certainly this requires more work than in languages like C++ that provide in-language support, but it can nevertheless be done in C.
我的影子我的梦 2024-08-03 04:45:02

高级和低级 C 文章包含很多很好的技巧。 特别是,请查看“类和对象”部分。

ANSI C 编码标准和风格 还包含一些很好的建议,您可以挑选。

The High and Low-Level C article contains a lot of good tips. Especially, take a look at the "Classes and objects" section.

Standards and Style for Coding in ANSI C also contains good advice of which you can pick and choose.

他是夢罘是命 2024-08-03 04:45:02
  1. 不要在头文件中定义变量; 相反,在源文件中定义变量并在标头中添加 extern 语句(声明)。 这将与#2 和#3 相关联。
  2. 在每个标头上使用包含防护。 这将省去很多麻烦。
  3. 假设您已完成#1 和#2,请在该文件中包含某个文件所需的所有内容(但仅包含您需要的内容)。 不要依赖编译器扩展 include 指令的顺序。
  1. Don't define variables in header files; instead, define the variable in the source file and add an extern statement (declaration) in the header. This will tie into #2 and #3.
  2. Use an include guard on every header. This will save so many headaches.
  3. Assuming you've done #1 and #2, include everything you need (but only what you need) for a certain file in that file. Don't depend on the order of how the compiler expands your include directives.
分开我的手 2024-08-03 04:45:02

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

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

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

The approach that Pidgin (formerly Gaim) uses is they created a Plugin struct. Each plugin populates a struct with callbacks for initialization and teardown, along with a bunch of other descriptive information. Pretty much everything except the struct is declared as static, so only the Plugin struct is exposed for linking.

Then, to handle loose coupling of the plugin communicating with the rest of the app (since it'd be nice if it did something between setup and teardown), they have a signaling system. Plugins can register callbacks to be called when specific signals (not standard C signals, but a custom extensible kind [identified by string, rather than set codes]) are issued by any part of the app (including another plugin). They can also issue signals themselves.

This seems to work well in practice - different plugins can build upon each other, but the coupling is fairly loose - no direct invocation of functions, everything's through the signaling stystem.

旧情勿念 2024-08-03 04:45:02

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

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

创建每个具有几个功能的小模块。 只公开你必须公开的内容,将模块内的其他内容保持静态。 将小模块与其 .h 接口文件链接在一起。

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

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

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

A function should do one thing and do this one thing well.

Lots of little function used by bigger wrapper functions help to structure code from small, easy to understand (and test!) building blocks.

Create small modules with a couple of functions each. Only expose what you must, keep anything else static inside of the module. Link small modules together with their .h interface files.

Provide Getter and Setter functions for access to static file scope variables in your module. That way, the variables are only actually written to in one place. This helps also tracing access to these static variables using a breakpoint in the function and the call stack.

One important rule when designing modular code is: Don't try to optimize unless you have to. Lots of small functions usually yield cleaner, well structured code and the additional function call overhead might be worth it.

I always try to keep variables at their narrowest scope, also within functions. For example, indices of for loops usually can be kept at block scope and don't need to be exposed at the entire function level. C is not as flexible as C++ with the "define it where you use it" but it's workable.

じ违心 2024-08-03 04:45:02

将代码分解为相关函数的库是保持事物井井有条的一种方法。 为了避免名称冲突,您还可以使用前缀来允许重用函数名称,尽管对于好的名称,我从未真正发现这是一个大问题。 例如,如果您想开发自己的数学例程,但仍使用标准数学库中的一些例程,则可以使用一些字符串作为前缀:xyz_sin()、xyz_cos()。

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

Breaking the code up into libraries of related functions is one way of keeping things organized. To avoid name conflicts you can also use prefixes to allow you to reuse function names, though with good names I've never really found this to be much of a problem. For example, if you wanted to develop your own math routines but still use some from the standard math library, you could prefix yours with some string: xyz_sin(), xyz_cos().

Generally I prefer the one function (or set of closely related functions) per file and one header file per source file convention. Breaking files into directories, where each directory represents a separate library is also a good idea. You'd generally have a system of makefiles or build files that would allow you to build all or part of the entire system following the hierarchy representing the various libraries/programs.

哀由 2024-08-03 04:45:02

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

There are directories and files, but no namespaces or encapsulation. You can compile each module to a separate obj file, and link them together (as libraries).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文