【问题标题】:Coding Standards for pure C (not C++)纯 C(非 C++)的编码标准
【发布时间】:2009-08-11 19:39:16
【问题描述】:

我来自 java 背景(来自我的 CS 课程)和一个学期的 C++。我刚刚为我的 Co-Op 完成了一个纯 C 语言的 OpenCV 项目,所以我问这个问题有点晚了。

纯C的设计流程和编码标准是什么?

我熟悉面向对象的编程、设计和最佳实践。我对像 C 这样的非面向对象的语言有点不知所措。每个变量和函数似乎都是全局的。这让我觉得它真的一团糟。

【问题讨论】:

  • 函数是“全局的”,但变量可以有不同的作用域...
  • 函数当然可以是全局的或文件静态的
  • @GMan,如果有足够多的不同用户编辑问题,它就会自己变成 CW。我已经添加了我的 CW 投票...
  • @bdonian 严重滥用编辑工具,恕我直言。

标签: c coding-style


【解决方案1】:

您可能有兴趣查看我不久前问过的similar question 的答案。此外,如果您对 C 风格指南感兴趣,您可能想看看 this page,因为它是 C(和 C++)风格指南的存储库。如果您想开怀大笑,请查看NASA C Style Guide。特别是,看看 massive 评论......你就会知道我在说哪一个。请不要这样写cmets。

我个人推荐Indian Hill C Style Guide 进行一些修改。此外,如果您在用 C 语言设计大型程序时遇到问题,您可能想购买本书 C Interfaces and Implementations

【讨论】:

  • +1 因为网页链接包含多个指向 C 标准的链接。
  • 遗憾的是,印度山的链接现在是 404。但是,谷歌搜索“印度山 c 风格指南 pdf”很快就会显示出许多可能的来源。 C and C++ Style Guides 似乎特别有用,至少对于像我这样的古玩爱好者来说。
  • 如果您正在寻找 NASA C 风格指南 笑声,请尝试第 64 页!
  • 印度山指南的链接无效。我在网络档案中找到了它,here
【解决方案2】:

老实说,我认为 StackOverflow 上没有多少答案 将教你如何设计和编写结构良好的 C 程序。你需要读一本好书,很明显要读的是 Kernighan & Ritchie 的The C Programming Language

【讨论】:

  • K&R 对设计流程和编码标准没有太多要说的。这是对 C 的一个很好的介绍。
【解决方案3】:

我没有 C 方面的专业经验(仅在 C++ 方面),所以不要太认真地对待我的建议、技巧和技巧,因为它们是“面向对象的”。

几乎是对象 C?

可以轻松模拟基本的类对象特征:

在标题中,前向声明您的类型,对其进行类型定义,并声明“方法”。例如:

/* MyString.h */

#include <string.h>

/* Forward declaration */
struct StructMyString ;

/* Typedef of forward-declaration (note: Not possible in C++) */
typedef struct StructMyString MyString ;

MyString *       MyString_new() ;
MyString *       MyString_create(const char * p_pString) ;
void             MyString_delete(MyString * p_pThis) ;
size_t           MyString_length(const MyString * p_pThis) ;

MyString *       MyString_copy(MyString * p_pThis, const MyString * p_pSource) ;
MyString *       MyString_concat(MyString * p_pThis, const MyString * p_pSource) ;

const char *     MyString_get_c_string(const MyString * p_pThis) ;
MyString *       MyString_copy_c_string(MyString * p_pThis, const char * p_pSource) ;
MyString *       MyString_concat_c_string(MyString * p_pThis, const char * p_pSource) ;

您会看到每个函数都有前缀。我选择“结构”的名称以确保不会与其他代码发生冲突。

您也会看到,我使用“p_pThis”来保持类似 OO 的想法。

在源文件中,定义你的类型,定义函数:

/* MyString.c */

#include "MyString.h"

#include <string.h>
#include <stdlib.h>

struct StructMyString
{
   char *      m_pString ;
   size_t      m_iSize ;
} ;

MyString * MyString_new()
{
   MyString * pMyString = malloc(sizeof(MyString)) ;

   pMyString->m_iSize = 0 ;
   pMyString->m_pString = malloc((pMyString->m_iSize + 1) * sizeof(char)) ;
   pMyString->m_pString[0] = 0 ;

   return pMyString ;
}

/* etc. */

如果您想要“私有”函数(或私有全局变量),请在 C 源代码中将它们声明为静态。这样,它们就不会在外面可见:

static void doSomethingPrivate()
{
   /* etc. */
}

static int g_iMyPrivateCounter = 0 ;

如果你想要继承,那你几乎完蛋了。如果您相信 C 中的所有内容(包括变量)都是全局的,那么您应该在尝试考虑如何模拟继承之前获得更多的 C 经验。

杂项。提示

避免使用多个代码路径。

例如,多重回报是有风险的。例如:

void doSomething(int i)
{
   void * p = malloc(25) ;

   if(i > 0)
   {
      /* this will leak memory ! */
      return ;
   }

   free(p) ;
}

避免非常量全局变量

这包括“静态”变量(不是静态函数)。

全局非常量变量几乎总是一个坏主意(例如,请参阅 C API strtok 以获取糟糕函数的示例),如果生成多线程安全代码,它们很难处理。

避免名称冲突

为您的函数和定义选择一个“命名空间”。这可能是:

#define GROOVY_LIB_x_MY_CONST_INT 42

void GroovyLib_dosomething() ;

小心定义

在 C 中无法避免定义,但它们会产生副作用!

#define MAX(a, b) (a > b) ? (a) : (b)

void doSomething()
{
   int i = 0, j = 1, k ;
   k = MAX(i, j) ;   /* now, k == 1, i == 0 and j == 1 */
   k = MAX(i, j++) ; /* now, k == 2, i == 0 and j == 3, NOT 2, and NOT 1 !!! */
}

初始化变量

避免在未初始化变量的情况下声明变量:

int i = 42 ; /* now i = 42 */
int j ;      /* now j can have any value */
double k ;   /* now f can have any value, including invalid ones ! */

未初始化的变量是导致痛苦错误的原因。

了解所有 C API

K&R 中描述的 C API 函数列表非常小。您将在 20 分钟内阅读整个列表。你必须知道这些函数。

想来点经验吗?

重写 C API。例如,尝试编写您自己版本的 string.h 函数,看看它是如何完成的。

【讨论】:

  • 我对任何以“我在 C 方面没有专业经验”开头的回答持谨慎态度。但是建议还可以。也许更好地解释你的观点和推理,以便其他人清楚为什么他们应该或不应该听从你的建议。
  • “继承”可以使用结构组合来伪造。 MSVC 将毫无问题地处理这个问题。 GCC 可能需要使用 -fms-extensions 选项。
【解决方案4】:

您可以通过将函数或对象的范围声明为“静态”来使其源文件本地化。这有点帮助。

否则,我看到的避免命名空间冲突的典型习惯用法是将某种设施标识符放在名称上。例如,foo.c 中的所有内容都可能命名为 foo_*

【讨论】:

    【解决方案5】:

    与其他优秀的 C 程序员合作。与他们一起进行代码审查。不仅让他们看你的代码,你还要看他们的代码。

    【讨论】:

      【解决方案6】:

      好消息是,您可以在 C 中以半面向对象的方式进行编程。您可以保护数据、公开访问函数等。它可能没有 C++ 的所有花哨,但从我所看到的其他人家的C++代码,反正很多人用不上。换句话说,人们在类中编写 C 代码,而在 C 中,您可以在没有类容器的情况下编写相同的代码。

      首先,读一本关于 C 编程和风格的书,K&R 很好。其次,我建议您查看CERT Programming Standard。尽管该站点主要关注“安全”编码标准,但这里的大部分内容都是每个人都应该遵循的一般代码质量标准。做这里提到的事情会提高你的质量,消除讨厌的错误,并且作为一个副作用,让你的代码更安全。

      【讨论】:

      【解决方案7】:

      您可能想好好看看 Linux 内核的源代码.....顺便说一句,这不是最容易开始的代码,但由于您具有编程背景,它可能会有所帮助...作为面向对象的程序员,您可能会特别发现 C 中的错误处理是一项艰巨的任务。可能这有帮助---> http://www.freetype.org/david/reliable-c.html

      【讨论】:

        【解决方案8】:

        您可以在纯 C 中进行面向对象的设计。一种简单的方法是将模块视为 class,将公共方法视为需要 this 参数作为显式第一个参数的普通函数。

        如果 class 名称是函数名称的前缀,并且所有私有函数和类数据都声明为 static,这将很有帮助。您使用malloc() 构建构造函数来获取内存,并显式初始化数据字段。

        具有完全私有数据成员的对象的构造函数可以公开一个不透明的指针(甚至类型为void *,或者如果需要类型安全,则作为指向不完整类型的指针)。如果您只想拥有公共数据成员,那么指向公共定义的struct 的指针就可以了。

        这个模式后面跟着一些库。初始化函数返回一个 cookie,该 cookie 必须传回所有库方法,其中一个方法用作析构函数。

        当然,在纯 C 中还有其他方法可以很好地设计,但如果 OO 适合你,你不必完全放弃它。

        【讨论】:

        • 请注意,这种设计唯一的面向对象的东西就是封装。继承和多态需要更多的特性,但是封装总是是一件好事,不管是什么语言。
        • @paercebal,真的。相当多的多态性可以用函数指针来完成,但是如果没有名字修饰的东西,你就不能重载。继承也可以直接建模。值得记住的是,毕竟 C++ 最初是作为编写纯 C 的预处理器实现的。
        【解决方案9】:

        您可以将文件范围变量和函数的可见性限制在它们各自的源文件中(尽管这不会阻止您传递指向这些对象的指针)。

        例如:

        /** foo.c */
        static void foo_helper() {...} /* foo_helper cannot be called by name 
                                          outside of foo.c */
        static int local_state;        /* local state is visible at file scope,
                                          but is not exported to the linker */
        

        【讨论】:

          【解决方案10】:

          如果您对关键环境标准感兴趣,那么您可能会对 MISRA C 感兴趣。我发现它很有帮助。

          您可以获得 MISRA C 2004 here

          更新的 MISRA C 2012 here 的简短概述。

          【讨论】:

          • 鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。请参阅:How to anwser
          • 感谢爱德华多的评论。 :) 我回答的要点只是向读者指出 MISRA C 标准,因为它被广泛使用。从我的角度来看,从上下文角度来看,这确实是所需的一切。这些链接在这里只是一个帮助。 :)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多