纯C(非C ++)的编码标准

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了纯C(非C ++)的编码标准相关的知识,希望对你有一定的参考价值。

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

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

我熟悉面向对象的编程,设计和最佳实践。我只是在像C这样的非面向对象语言上有点不知所措。每个变量和函数看起来都是全局的。这让我感觉真的很乱。

答案

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

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

另一答案

老实说,我认为StackOverflow上的任何答案都不会教你如何设计和编写结构良好的C程序。你需要阅读一本好书,显而易见的是Kernighan&Ritchie撰写的The C Programming Language

另一答案

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

几乎是对象C?

可以轻松地模拟基本类似对象的功能:

在标题中,forward声明你的类型,typedef它,并声明“方法”。例如:

/* 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) ;

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

你也会看到我用“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语言的经验。

杂项。提示

Avoid multiple code-paths.

例如,多次退货是有风险的。例如:

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

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

   free(p) ;
}

Avoid non-const globals

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

全局非const变量几乎总是一个坏主意(例如,参见C API strtok作为一个蹩脚函数的例子),如果生成多线程安全代码,它们很难处理。

Avoid name collision

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

#define GROOVY_LIB_x_MY_CONST_INT 42

void GroovyLib_dosomething() ;

Beware defines

在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 !!! */
}

Initialize your variables

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

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 ! */

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

Know all the C API

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

Wanna some experience?

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

另一答案

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

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

另一答案

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

另一答案

好消息是你可以用C语言进行半对象编程。你可以保护数据,暴露访问函数等。它可能没有C ++的所有功能,但是从我看到的其他人的C ++代码很多人不管怎样都不习惯。换句话说,人们在一个类中编写C代码,在C中你可以编写没有类容器的相同代码。

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

另一答案

你可能想要好好看看Linux内核的来源..... BTW它不是最简单的代码片段,但由于你来自编程背景,它可能会有所帮助......作为一个对象面向程序员,您可能会特别发现C中的错误处理是一项艰巨的任务。可能这有助于---> http://www.freetype.org/david/reliable-c.html

另一答案

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

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

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

这种模式之后是许多库。初始化函数返回一个必须传递回所有库方法的cookie,一个方法用作析构函数。

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

另一答案

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

例如:

/** 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 */

以上是关于纯C(非C ++)的编码标准的主要内容,如果未能解决你的问题,请参考以下文章

《安富莱嵌入式周报》第279期:强劲的代码片段搜索工具,卡内基梅隆大学安全可靠C编码标准,Nordic发布双频WiFi6 nRF7002芯片

C 和 C++ 编码标准

读C#代码整洁之道笔记01_C#的编码标准和原则

MATLAB C 生成编码器可以生成适合嵌入式系统的 C 代码吗?

从目标 C 中的图像读取非标准属性

如何有条件地将 C 代码片段编译到我的 Perl 模块?