C中变量和函数的命名约定


13

在CI中编写大型项目时遇到了问题。如果我继续编写更多的代码,那么有时候我将很难组织代码。我的意思是,程序不同部分的函数和变量的命名似乎混淆了。

因此,我在考虑是否存在可以用于C变量和函数的有用的命名约定?

大多数语言都建议使用命名约定。但是到目前为止,对于C来说,我唯一读到的是名称应具有描述性,以提高代码的可读性。

编辑:

建议的命名约定的一些示例的示例:

我在某处阅读了一些其他的Java命名约定,但不记得在哪里。


列举一些带有建议命名约定的语言示例。在哪里可以找到这些命名约定。
菲利普(Philip)

@Philip添加了示例
Aseem Bansal 2013年

1
变量不应该有问题,因为您不使用全局变量。对于函数名称:如果模块的名称为order.c,则可以命名函数order_add()order_del()诸如此类。可能会有一些旧的系统告诉您名称必须在前8个字符内唯一。当您切换到C ++后来意外,你爱写order::add()order::del()再。
ott--

Answers:


17

如果我继续编写更多的代码,那么有时候我将很难组织代码。

这是您的问题:组织正确,样式应该更容易流动。

不要等待组织代码:随时随地组织代码。尽管该语言无法满足您的要求,但仍应将代码组织成具有低耦合度和高内聚性的模块。

这些模块自然会提供一个名称空间。缩写模块名称(如果很长),并在模块名称前加上函数名称,以避免冲突。

在单个标识符的级别上,这些标识符大致按主观顺序递增:

  1. 选择一个约定并坚持下去
    • 例如,function_like_this(struct TypeLikeThis variable)很常见
  2. 绝对避免使用匈牙利表示法(对不起,JNL)

    • 除非您愿意按原计划使用它,这意味着Simonyi的应用程序符号而不是糟糕的系统版本

      为什么?我可以写一篇有关此的文章,但我建议您阅读Joel Spolsky的这篇文章,如果有兴趣的话,再找一些其他文章。底部有一个指向Simonyi原始论文的链接。

  3. 避免使用指针typedef,除非它们是真正不透明的cookie类型-它们只会使事情变得混乱

    struct Type *ok;
    typedef struct Type *TypePtr;
    TypePtr yuck;
    

    不透明的Cookie类型是什么意思?我的意思是必须在模块(或库或其他任何东西)中使用的东西必须传递给客户端代码,但该客户端代码不能直接使用。它只是将其传递回库。

    例如,数据库库可能会公开类似

    /* Lots of buffering, IPC and metadata magic held in here.
       No, you don't get to look inside. */
    struct DBContextT;
    /* In fact, you only ever get a pointer, so let's give it a nice name */
    typedef struct DBContexT *DBContext;
    
    DBContext db_allocate_context(/*maybe some optional flags?*/);
    void db_release_context(DBContext);
    int db_connect(DBContext, const char *connect);
    int db_disconnect(DBContext);
    int db_execute(DBContext, const char *sql);
    

    现在,上下文对于客户端代码是不透明的,因为您无法查看内部。您只需将其传递回库。诸如此类的东西FILE也是不透明的,并且整数文件描述符也是cookie,但也不是不透明的。


设计注意事项

我在上面使用了“ 低耦合和高内聚力 ”一词,没有任何解释,对此我感到很不好。您可以搜索它,并且可能会找到一些不错的结果,但是我将尝试简要地解决它(同样,我可以写一篇文章,但会尝试不这样做)。

上面概述的数据库库显示出较低的耦合度,因为它向外界公开了一个小的接口。通过隐藏其实现细节(部分使用不透明的cookie技巧),它可以防止客户端代码依赖于那些细节。

想象一下,而不是不透明的cookie,我们声明上下文结构,以便其内容可见,其中包括用于与数据库的TCP连接的套接字文件描述符。如果我们随后在数据库运行在同一台机器上时更改实现以支持使用共享内存段,则需要重新编译客户端,而不仅仅是重新链接。更糟糕的是,客户端可能已经开始使用文件描述符,例如调用setsockopt更改默认缓冲区大小,现在它也需要更改代码。所有这些详细信息都应在可行的情况下隐藏在我们的模块内部,这样可以降低模块之间的耦合。

该示例还显示出很高的凝聚力,因为模块中的所有方法都与同一任务(数据库访问)有关。这意味着只有需要了解实现细节(即cookie的内容)的代码才可以实际访问它们,从而简化了调试。

您还可以看到,只需关注一个问题,就可以轻松选择前缀来将这些功能分组在一起。

现在,说这个例子很好是很容易的(特别是因为它还不完整),但是并不能立即为您提供帮助。诀窍是在编写和扩展代码时观察执行相似操作或在相同类型上运行的函数(可能是其自身模块的候选对象),以及执行许多其他未完成的单独操作的函数。确实相关,并且可能是拆分的候选者。


您能帮助我理解为什么避免匈牙利人吗?只是想知道更多有关它的信息。:)
JNL

@JNL:评论太短,无法正确解释。我建议您将其发布为新问题。
Bart van Ingen Schenau 2013年

with low coupling and high cohesion。这意味着什么?并请说明有关不透明Cookie类型的信息。我不知道那是什么意思。
Aseem Bansal 2013年

我试图简短地解决这两个问题,坦率地说,坦率地说失败了。希望它可以帮助您入门。
无用的

几天后我要答复。抱歉 我阅读了您对的描述low coupling and high cohesion。因此,它基本上意味着在可能的时候封装事物,并且应该以实际需要的功能可以访问的方式来完成。有些事情困扰着我,但我仍然认为我明白你的意思。
Aseem Bansal

5

我认为,只要牢记以下三点,便可以解决90%的命名问题:a)使变量和函数名称尽可能具有描述性, b)在整个代码中保持一致(即,如果函数名为addNumbers,则a第二个函数应该命名为multipleNumbers,而不是numbersMul)。c)尽可能使名称简短,因为我们需要键入它们。

话虽这么说,但如果您想了解该主题的其他方面,则有关命名约定的Wikipedia页面上列出了许多您应牢记的事项。它还在C和C ++上有特色:

在C和C ++中,关键字和标准库标识符大多为小写字母。在C标准库中,缩写名称是最常见的名称(例如,isalnum用于测试字符是否为字母数字),而C ++标准库通常使用下划线作为单词分隔符(例如out_of_range)。按照惯例,代表宏的标识符仅使用大写字母和下划线编写(这与许多编程语言中使用全大写标识符作为常量的惯例有关)。包含双下划线或以下划线和大写字母开头的名称保留用于实现(编译器,标准库),不得使用(例如,保留__或_Reserved)。[5] [6] 从表面上看,它与stroping类似,但语义不同:


3
“如果可能的话,尽量使名称简短”使用具有自动补全功能的IDE,然后您的函数名称就可以像它们需要的那样长且具有描述性,您只需要键入一次即可。
乔尔

1
@乔尔可怕的建议。并非每个人都会使用与您相同的IDE。
詹姆斯

6
@James他们不需要,他们可以使用任何不错的IDE。然后,您不必为了提高效率而牺牲清晰度。
乔尔

现在,IDE的术语有些稀疏了。从技术上讲,Notepad ++是一个IDE,因为您可以对其进行配置以编译和运行您的项目,但它主要是一个文本编辑器。并自动完成。
菲利普(Philip)

5

C语言中唯一的硬约束是没有名称空间。因此,你必须找到一种方法,让rename()你的功能的文件系统库从不同rename()的功能的媒体库。通常的解决方案是前缀,例如:filesystem_rename()media_rename()

其他一般建议是:在项目或团队中保持一致。可读性将得到提高。


+1:对于库中导出的符号尤其如此。“我很抱歉,但该文件系统库不与媒体库去,因为两者都具有导出函数命名。
渣油

2

如果您正在寻找全球认可的格式

MISRA / JSF / AUTOSAR涵盖了几乎所有100%的用于命名和组织C / C ++代码的行业标准。问题在于它们将不会免费获得,即每本指南都需要花费一些钱。我知道MISRA 2008 C / C ++编码标准书的价格约为50美元。

当您写日记时,您可以将它们视为哈佛参考书目和加法阅读。我使用过MISRA,这是命名函数和变量并进行合理组织的好方法。

如果您正在寻找临时的东西

我猜您为Python和Java提供的引用是可以的。我见过人们采用javadoc样式进行注释,命名和组织代码。事实上,在我的上一个项目中,我不得不用类似Java的函数/变量名来编写C ++代码。其背后的两个原因:

1)显然更容易理解。

2)生产代码要求没有符合安全关键软件系统标准的要求。

3)旧版代码(以某种方式)就是这种格式。

4)Doxygen允许Javadoc sytle注释。那时,我们正在使用doxygen为生产人员生成文档。

许多程序员都会对此表示反对,但我个人认为在C / C ++中采用javadoc样式函数/变量命名没有错。是的,无论如何,都需要解决组织流量控制,线程安全性等问题的做法。但是,我不是这里的申请人。我也不知道您的生产代码格式要求有多严格。在不将其转移到主题之外的情况下,建议您检查一下要求,了解您对特定命名约定的依赖程度,并使用我的解决方案和其他答案中提到的解决方案

希望这对您有所帮助!?


其实我是在问个人C代码。但我会记住你的建议。
Aseem Bansal

@AseemBansal无论是个人还是专业人士,他们都很容易学习,也很容易放在您的简历上:) ....由您决定。
hagubear13年

0

命名时要考虑的重要事项很少;

  1. 查看actionObject或ObjectAction类型。(Object不用于C。但是通常,当您使用其他面向对象的语言时), 这应该有所帮助

  2. 休息肯定是一致的,简短的和描述性的。

  3. 同样,要定义每个变量和函数的唯一目的,例如:如果要临时存储一个值,请将其命名为int的nTempVal
  4. 变量应为名词,方法应为动词。

6
匈牙利符号(在变量前面加上字母表示类型)不会使您感到痛苦。值得庆幸的是,它已经过时了。

@StevenBurnap只是好奇为什么要避免使用匈牙利格式?我相信这就是他们在学校教给我们的东西,我也在一些工作场所也看到过这样的代码。如果不是匈牙利人,您会推荐哪一个。谢谢
JNL

1
最好的命名约定只是一贯使用的名称,理想情况下使用清晰,描述性的名称来使其相对简短,而不必过多缩写,并避免使用多余的前缀。匈牙利符号的实际用途很少,使代码更难阅读,并且使更改类型更难。

2
这是对匈牙利意图的原始意图和可憎性的描述:joelonsoftware.com/articles/Wrong.html
Residuum 2013年

@Residuum那是一个很好的链接。帮助很大。欣赏它。
JNL 2013年

0

大多数答案都是好的,但是我想说一些关于库和包含文件的命名约定的事情,类似于在其他语言(例如C ++或Java)中使用命名空间:

如果要构建库,请为导出的符号找到一个通用前缀,即全局函数,typedef和变量。这将防止与其他库发生冲突,并确定这些功能来自您的库。这是一点匈牙利语的应用程序。

也许走得更远,对导出的符号进行分组:libcurl将curl_ *用于全局符号,将curl_easy _ *,curl_multi_ *和curl_share_ *用于不同的接口。因此,除了对所有函数使用curl_ *之外,他们还为不同的接口添加了另一级别的“命名空间”:现在,在curl_multi_ *句柄上调用curl_easy_ *函数看起来是错误的,请参见http:// curl上的函数名称。 haxx.se/libcurl/c/

保留导出符号的规则,应将这些规则用于#includeed文件中的静态函数:尝试为这些函数找到通用前缀。也许您在名为“ my_string”的文件中具有静态字符串实用工具功能?使用my_string_ *前缀所有这些函数。


如果我是对的,那么用导出的符号表示全局变量,函数,typedef等。您能否解释一下有关导出符号分组的内容?我以为您已经在上一段中进行了解释。您在第三段中添加了什么?
Aseem Bansal 2013年
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.