C的设计原理,最佳实践和设计模式(或一般而言,过程编程)?[关闭]


91

在设计C项目时,可以遵循哪些已知的设计原则,最佳实践和设计模式?还是总体上对过程式(命令式)编程有用的设计原则?

(我是“面向对象的一代”的孩子,必须第一次设计一个大型的C项目)


1
:你可能有兴趣在anwsers这个问题stackoverflow.com/questions/661307/...
mouviciel

7
在发表我的问题之前,我做了一些互联网和大学图书馆的研究,而且绝对不会被有关C语言的软件设计的书籍所淹没。我问你最喜欢的(不是谈论通用的C书籍,不是谈论诸如有意义的变量之类的编码约定)名称,但涉及更高的抽象,软件架构级别)。此外,我不同意您对他人的指责。您是说每个程序员都应该自己了解最佳实践和良好的设计模式?这肯定是一个必须使用他人经验的问题。
Dimi

2
抱歉,Dimi,不是特别关心您,我也不太清楚。这些东西过去常常像其他任何方式一样被口头传承:没有一套名义上的官方“模式”,乔纳森的答案就是你在书中找到的,但是每个人都知道信息隐藏。似乎口头的传统正在丢失,许多年轻的程序员认为OOP发明了封装和分离。这个社区似乎对自己的历史了解不如我所愿。因此,我承认自己处在脾气暴躁的老人区。
dmckee ---前主持人小猫,2010年

1
我无法分享您的回顾性观点,因为只能在实地找到自己的脚,但是我接受您的建议。感谢您更清楚地表达自己,这对于阅读有经验的人的观点总是很有价值。非常感谢您的贡献。
Dimi

SEI CERT C编码标准提供了一套好的规则和常见的良好做法,以及您应避免使用的东西。
拉米

Answers:


65

信息隐藏-如Parnas(软件基础知识)所倡导的。

仔细管理标题和可见性:

  • 源文件中所有可能对外界隐藏的东西都应该是;仅应公开记录的外部接口。
  • 公开的所有内容都在标头中声明。
  • 该标头用于需要功能的地方(及其定义位置)。
  • 标头是独立的-当您需要它时,可以使用它,而不必担心“我还必须包含哪些其他标头”,因为标头可通过包含需要制作的任何东西来确保其正常工作工作。
  • 标头是自我保护的-因此,是否包含多次标头并不重要。

    #ifndef HEADER_H_INCLUDED
    #define HEADER_H_INCLUDED
    ...rest of header contents, including other #include lines if necessary
    #endif /* HEADER_H_INCLUDED */
  • 设计要在“对象”(通常是结构)上工作的函数集-并使用这些函数,而不是在使用结构的代码中随意查找结构的内部。可以将其视为自我强加的封装。


好点,谢谢你,乔纳森。抽象数据类型是信息隐藏的另一个很好的示例,它清楚地区分了用法和实现(已知的外部接口和未知的内部实现)。
Dimi

23

我的三个建议:

  • 编写单元测试。他们将帮助您根据实际情况选择适合您问题的设计。比仅依靠预先思考的思维要好得多。
  • 从第一天开始安装并运行内存泄漏检测器(那里有各种各样的库)。一旦程序/测试退出,请让此库打印所有泄漏。这将使您在引入泄漏后立即发现泄漏,从而减少了痛苦。
  • 用C编写OOP代码。没有那么困难。虽然可以模拟方法覆盖,但我建议您从模拟简单对象开始。即使是这种简单的机制也可以为您带来很大的收益。

这是一个例子:

typedef struct Vector {
  int size;
  int limit;
  int* ints; 
} Vector;

Vector* Vector_new() {
  Vector* res = (Vector*) malloc(sizeof(Vector));
  res->limit = 10;
  res->size = 0;
  res->ints = (int*) malloc(sizeof(int) * res.limit);

  return res;
}


void Vector_destroy(Vector* v) {
  free(v->ints);
  free(v);
}

void Vector_add(Vector* v, int n) {
  if(v->size == v->limit) {
    v->limit = v->limit * 2 + 10;
    v->ints = realloc(v->ints, v->limit);     
  }

  v->ints[v->size] = n;
  ++v->size;
}

int Vector_get(Vector* v, int index) {
  if(index >= 0 && index < v->size)
    return v->ints[index];

  assert false;
}

谢谢,伊泰 我会按照你的建议。
迪米

1
4.不要转换的结果malloc
SS安妮

22

有本很好的免费在线书籍,标题为《使用ANSI-C进行面向对象的编程》,其中涵盖了用C语言编写面向对象的代码的主题。谷歌搜索 “面向对象的C”也带来了许多其他好处示例和资源。

如果您的项目对安全至关重要,则MISRA-C是一套很好的规则。它主要用于嵌入式c,但在其他领域也可能有用。

我认为自己是OO编码器,并且我使用Embedded-C做了大量工作。我能给出的最佳建议,尤其是对于大型项目,不要过分。在ANSI C之上创建一个完整的OO框架可能很诱人,但是要使其正确,需要花费大量的时间和精力。获得的技巧越多,您将花费更多的时间来调试框架而不是在实际项目上工作。头脑清晰,对YAGNI扎实,扎实地处理任务。祝你好运!


谢谢,詹姆斯。我不想在ANSI C之上创建面向对象的框架,而是希望找到特殊且适当的过程编程设计原则。MISRA-C提示非常有用,特别是因为它实际上是一个嵌入式项目。我将仔细研究它。
Dimi

啊,嵌入式C的乐趣。不要忘记,您必须在函数的顶部(或任何{ }块的顶部)声明变量。那总是咬我一两次:)
e.James

7

OOP是一种方法而非技术。因此,我的第一点建议就是不要将其视为过程编程。

出于e.James的观点,您不想尝试重新创建面向对象的语言,也不想假装自己具有该功能。您仍然可以通过遵循一些简单的原则来做所有正确的事情:

  1. 试驾一切。
  2. 找到不同的内容并将其封装。
  3. 设计界面。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.