【问题标题】:Interface/Implementation in ANSI CANSI C 中的接口/实现
【发布时间】:2014-11-30 01:39:46
【问题描述】:

我正在使用 C 语言开发一个大型项目,我想使用接口 (.h) 和实现 (.c) 文件来组织它,类似于许多面向对象的语言,例如 Objective-C 或 Java。我熟悉在 C 中创建静态库,但我认为为我的项目这样做是不必要的复杂。如何在 ANSI C 中实现接口/实现范例?我主要使用 GCC 进行编译,但我的目标是严格遵守 ANSI C 和交叉编译器兼容性。谢谢!

【问题讨论】:

  • 你真的是在谈论用 C 语言做 OOP 吗?
  • 不,我基本上是在寻找一种将函数定义与所述函数的内部工作分开的方法,以及一种将相关函数等组合在一起的方法(例如 math.h)。我不想使用库,因为我不会在其他程序中重用函数,而且我可能还需要对功能进行频繁的更改。
  • 在.h文件中将函数设为静态,只在.c文件中可见;
  • 当您将函数声明放入 .h 文件并将定义放入 .c 文件时,这不就是您已经得到的吗?
  • extern 函数在文件外部可见,当您包含它时。静态函数仅在实现中可见。

标签: c ansi interface-implementation


【解决方案1】:

听起来您已经在做正确的事:好的 C 代码在 .h 文件中组织接口,在 .c 文件中组织实现。

示例 a.h 文件:

void f(int a);

示例 a.c 文件:

#include "a.h"
static void helper(void) {...}
void f(int a) {... use helper()...}

main.c 文件示例:

#include "a.h"
int main(void) { f(123); return 0; }

你获得了模块化,因为辅助函数没有在头文件中声明,所以其他模块不知道它们(如果你愿意,你可以在 .c 文件的顶部声明它们)。拥有这种模块化减少了需要重新编译的次数,并减少了需要重新编译的多少。 (链接必须每次都完成)。请注意,如果您没有在标头中声明辅助函数,那么您已经很安全了,但是在它们前面放置 static 也会在链接期间将它们从其他模块中隐藏起来,因此如果多个模块使用相同的辅助函数,则不会发生冲突-函数名称。

如果您只使用原始类型,那么您需要知道的就是这些,您可以在此处停止阅读。但是,如果您的模块需要使用结构,那么它会变得更加复杂。

有问题的示例标题 b.h:

typedef struct Obj {
    int data;
}*Obj;
Obj make(void);
void work(Obj o);

您的模块想要传入和传出对象。这里的问题是,内部结构被泄露给依赖这个头文件的其他模块。如果表示更改为float data,则所有使用模块都必须重新编译。解决此问题的一种方法是仅使用void*。这就是有多少程序可以做到的。然而这很麻烦,因为每个将void* 作为参数的函数都必须将其转换为Obj。另一种方法是这样做:

标题 c.h:

typedef struct Obj*Obj;
Obj make(void);
void work(Obj);

实施 c.c:

#include "c.h"
typedef struct Obj {
    int data;
}*Obj;

之所以可行,是因为 Obj 是一个指针(而不是按值/复制的结构)。其他依赖该模块的模块只需要知道传入传出的指针,而不需要知道它指向什么。

【讨论】:

  • 感谢您的明确答复。这可以用 GCC 中的一个命令来完成吗?简单地调用“gcc main.c”会产生一个未定义的符号错误。
  • 您应该为每个 c 文件调用 gcc -c filename.c。它们中的每一个都会生成一个 .o 文件(模块)。最后,您调用gcc *.o 将它们链接在一起。如果您只更改了b.c,那么只需重新编译它(b.o),然后再次链接。 (未更改的c文件不需要再次编译)
  • @user2105505 同样,这通常由生成文件自动执行。这样你只需要在命令行输入“make”,它就会选择要重新编译的c文件,然后将程序链接在一起。
  • 在 C 中,不要在函数声明中使用空括号(不是定义的一部分); Obj make(); 告诉编译器该函数采用未指定数量的参数受默认参数提升;正确的原型应该是Obj make(void);
  • @eznme:在 C 中,不是品味问题——一个是原型,另一个不是;例如echo "void foo(); int main() { foo(42); }" | clang -fsyntax-only -Weverything -xc - 将毫无怨言地解析;添加void,它不会
【解决方案2】:

您必须阅读有关非 OOL 的 OOP 的内容,例如 http://www.cs.rit.edu/~ats/books/ooc.pdf。 但是,这样做您将永远不会拥有强大的 OOP 类型。

【讨论】:

  • 另外,这样做会让你发疯;)
  • 这有点夸张,但你至少需要严格的纪律(这可能会让你发疯)。
  • “我很高兴发现 ANSI-C 是一种全面的面向对象的语言”——停止阅读。好笑。是的,可以通过大量的努力来模拟 OOP 模式,但这几乎是毫无意义且效率低下的,而且:这基本上是在重新发明一个轮子。在 C 中不需要 OO 模式。C 几乎是一种低级语言,强制使用 OO 只会引发问题——这就是 Obj-C 被发明的原因。
  • 谢谢你毁了我的夜晚。
【解决方案3】:

请帮自己一个忙,阅读C Interfaces and Implementations: Techniques for Creating Reusable Software

这里是a repository of mine,其中包含一些使用本书中描述的接口和实现模式用 C 编写的库。

【讨论】:

    猜你喜欢
    • 2011-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    • 2016-03-02
    • 2012-03-01
    相关资源
    最近更新 更多