【问题标题】:How should I structure complex projects in C? [closed]我应该如何用 C 构建复杂的项目? [关闭]
【发布时间】:2010-10-14 06:17:54
【问题描述】:

我只有初级 C 技能,我想知道是否有任何事实上的“标准”来用 C 构建一个有点复杂的应用程序。甚至是基于 GUI 的应用程序。

我一直在 Java 和 PHP 中使用 OO 范例,现在我想学习 C,我担心我可能会以错误的方式构建我的应用程序。我不知道应该遵循哪些准则来实现模块化、解耦和程序化语言的枯燥。

你有什么推荐的读物吗?我找不到任何适用于 C 的应用程序框架,即使我不使用框架,我总是通过浏览他们的代码找到好主意。

【问题讨论】:

标签: c project-management


【解决方案1】:

一种常见的误解是,OO 技术不能在 C 中应用。大多数都可以——只是它们比专门用于该工作的语法的语言更笨拙。

稳健系统设计的基础之一是将实现封装在接口后面。 FILE* 和与之一起工作的函数(fopen()fread() 等)是如何在 C 中应用封装来建立接口的一个很好的例子。 (当然,由于 C 缺少访问说明符,您不能强制没有人在 struct FILE 内窥视,但只有受虐狂才会这样做。)

如有必要,可以使用函数指针表在 C 中实现多态行为。是的,语法很丑但是效果和虚函数一样:

struct IAnimal {
    int (*eat)(int food);
    int (*sleep)(int secs);
};

/* "Subclass"/"implement" IAnimal, relying on C's guaranteed equivalence
 * of memory layouts */
struct Cat {
    struct IAnimal _base;
    int (*meow)(void);
};

int cat_eat(int food) { ... }
int cat_sleep(int secs) { ... }
int cat_meow(void) { ... }

/* "Constructor" */
struct Cat* CreateACat(void) {
    struct Cat* x = (struct Cat*) malloc(sizeof (struct Cat));
    x->_base.eat = cat_eat;
    x->_base.sleep = cat_sleep;
    x->meow = cat_meow;
    return x;
}

struct IAnimal* pa = CreateACat();
pa->eat(42);                       /* Calls cat_eat() */

((struct Cat*) pa)->meow();        /* "Downcast" */

【讨论】:

  • 纯 C 编码器在阅读此代码时会迷失方向...
  • @mouviciel:垃圾!大多数 C 编码人员都理解函数指针(或者至少他们应该理解),除此之外没有什么真正发生的事情。至少在 Windows 上,设备驱动程序和 COM 对象都以这种方式提供它们的功能。
  • 我的意思不是无能,而是不必要的复杂化。函数指针对于 C 编码器很常见(例如,回调),继承不是。我更喜欢用 C 编写的 C++ 编码器利用时间来学习 C,而不是构建伪 C++ 类。也就是说,您的方法在某些情况下可能有用。
  • 实际上,您甚至可以使用 C++ pimpl 成语模拟访问说明符。如果一个类型的 private 成员被封装在一个只在实现中可见的类型(又名“.c 文件”)中,接口的用户将很难更改它们(当然,如果他们想故意搞砸你,他们可以向 pimpl 指针写垃圾,但你可以在 C++ 中做同样的事情。
  • 投反对票者:愿意发表评论吗?
【解决方案2】:

我建议首先阅读 C/C++ 教科书。例如,C Primer Plus 就是一个很好的参考。浏览这些示例将为您提供有关如何将您的 java OO 映射到更程序化的语言(如 C)的想法。

【讨论】:

    【解决方案3】:

    所有好的答案。

    我只会添加“最小化数据结构”。这在 C 中甚至可能更容易,因为如果 C++ 是“带类的 C”,那么 OOP 会试图鼓励您将每个名词/动词都放在脑海中,并将其转变为类/方法。这可能非常浪费。

    例如,假设您有一组时间点的温度读数,并且您希望在 Windows 中将它们显示为折线图。 Windows 有一条 PAINT 消息,当您收到它时,您可以循环执行 LineTo 函数的数组,并在执行过程中缩放数据以将其转换为像素坐标。

    我完全见过太多次了,由于图表是由点和线组成的,人们会建立一个由点对象和线对象组成的数据结构,每个对象都可以进行 DrawMyself,然后使其持久化,在理论认为这在某种程度上“更有效”,或者他们可能,只是可能,必须能够将鼠标悬停在图表的某些部分上并以数字方式显示数据,因此他们将方法构建到对象中来处理这个问题,并且,当然,这涉及到创建和删除更多对象。

    因此,您最终会得到大量可读性极强的代码,并且仅将 90% 的时间用于管理对象。

    所有这些都是以“良好的编程实践”和“效率”的名义完成的。

    至少在 C 语言中,简单、有效的方法会更加明显,而构建金字塔的诱惑力会不那么强烈。

    【讨论】:

    • 喜欢你的回答,这是完全正确的。 OOP 必须死!
    • @JoSo:我使用 OOP,但很少。
    • 对我来说“最低限度”(虽然我不知道这对你意味着什么)并不能真正算作 OOP,它是面向对象的 编程 - 默认情况下是对象.
    • 我也使用了一些“OOP”。主要针对我的 C 模块,其中很多都有 init() 和 exit() 例程。
    【解决方案4】:

    无论使用哪种开发语言,封装始终是成功开发的关键。

    我用来帮助在 C 中封装“私有”方法的一个技巧是不在“.h”文件中包含它们的原型。

    【讨论】:

      【解决方案5】:

      关键是模块化。这更容易设计、实现、编译和维护。

      • 识别应用中的模块,例如 OO 应用中的类。
      • 为每个模块单独的接口和实现,只在接口中放入其他模块需要的东西。请记住,C 中没有命名空间,因此您必须使接口中的所有内容都是唯一的(例如,带有前缀)。
      • 在实现中隐藏全局变量并使用访问器函数进行读/写。
      • 不要考虑继承,而是考虑组合。作为一般规则,不要试图在 C 中模仿 C++,这将非常难以阅读和维护。

      如果您有时间学习,请查看 Ada 应用程序的结构,其中包含强制性的package(模块接口)和package body(模块实现)。

      这是用于编码的。

      为了维护(记住你编码一次,但你维护了几次)我建议记录你的代码; Doxygen 对我来说是个不错的选择。我还建议构建一个强大的回归测试套件,它允许您进行重构。

      【讨论】:

        【解决方案6】:

        如果您知道如何用 Java 或 C++ 构建代码,那么您可以遵循与 C 代码相同的原则。唯一的区别是您身边没有编译器,您需要手动完成所有操作。

        由于没有包和类,因此您需要从仔细设计模块开始。最常见的方法是为每个模块创建一个单独的源文件夹。您需要依靠命名约定来区分不同模块之间的代码。例如,在所有函数前面加上模块的名称。

        您不能使用 C 来创建类,但您可以轻松实现“抽象数据类型”。您为每种抽象数据类型创建一个 .C 和 .H 文件。如果您愿意,可以有两个头文件,一个是公共的,一个是私有的。这个想法是所有需要导出的结构、常量和函数都到公共头文件中。

        你的工具也很重要。一个有用的 C 工具是lint,它可以帮助您发现代码中的异味。您可以使用的另一个工具是 Doxygen,它可以帮助您生成documentation

        【讨论】:

          【解决方案7】:

          GNU coding standards 已经发展了几十年。阅读它们是一个好主意,即使你没有完全按照它们来做。思考其中提出的要点可以为您构建自己的代码奠定更坚实的基础。

          【讨论】:

          • 不是每个人都喜欢它们,来自lxr.linux.no/linux+v2.6.29/Documentation/CodingStyle:“首先,我建议打印出一份 GNU 编码标准的副本,而不是阅读它。烧掉它们,这是一个很好的象征性姿态” .我已经很多年没有阅读它们了,但 Linus 有一些有效的反对意见。
          • @hlovdal:当然,并不是每个人都喜欢任何一种特定的编码标准,这就是为什么对于类似的用例有不止一种标准。重要的部分是您在自己的项目中保持一致,至少遵循一些标准,而不是事实上的临时不一致。
          【解决方案8】:

          复杂应用的数字规则:应该易于阅读。

          为了简化复杂的应用程序,我使用Divide and conquer

          【讨论】:

            【解决方案9】:

            我建议您查看任何流行的开源 C 项目的代码,例如...嗯... Linux 内核或 Git;看看他们是如何组织的。

            【讨论】:

              猜你喜欢
              • 2015-11-05
              • 1970-01-01
              • 2018-09-27
              • 1970-01-01
              • 1970-01-01
              • 2021-02-21
              • 2020-02-03
              • 1970-01-01
              • 2014-08-08
              相关资源
              最近更新 更多