器→工具, 编程语言

PEP 7:C语言风格指南

钱魏Way · · 462 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

PEP 全称是 Python Enhancement Proposal,翻译成中文是 Python 改进提案。为什么会有 C 语言的风格指南?原因是 Python 本身是由 C 语言实现的。这里整理的是PEP 7 — Style Guide for C Code 的翻译。

介绍

这篇文档给出了 C 语言编码的风格约定,包括 Python 中用 C 实现的部分。关于 Python 编码风格的约定,请参阅PEP-8

注意,有些约定并不一定要恪守。下面是打破约定的两个很好的理由:

  • 某个约定使得代码难以阅读,即使是对于那些习惯于阅读风格良好的代码的人。
  • 某个约定使得新增代码与已有代码不一致(可能是历史原因导致的)。尽管这也是一个清理别人糟糕代码的好机会。

C 语言版本

  • 使用 ISO/ANSI 标准 C(1989 年版本)。这意味着所有声明必须放在某个块的顶部(并不一定是函数的顶部)。
  • 不使用 GCC 的扩展(例如,不要写没有尾部反斜杠的多行字符串)。
  • 所有函数声明和定义必须使用完整的原型(即,明确写出所有参数的类型)
  • 不使用 C++ 风格的 // 单行注释
  • 对于主流的编译器(如 gcc, VC++ 等),编译时不能有警告

代码布局

  • 使用 4 个空格作为缩进,不使用 tab
  • 每行不超过 79 个字符。如果这和上一条规则让你没有空间去编码,那你的代码太复杂了–考虑写成子程序
  • 每行都不应该以空格结尾。如果你认为你的代码中有一些行末的空格很重要,再想想吧–别的编辑器可能会自动删掉它们。
  • 函数定义风格:函数名写在行首,最外部的大括号写在行首,声明局部变量后空一行。
static int
extra_ivars(PyTypeObject *type, PyTypeObject *base)
{
    int t_size = PyType_BASICSIZE(type);
    int b_size = PyType_BASICSIZE(base);

    assert(t_size >= b_size); /* type smaller than base! */
    ...
    return 1;
}
  • 代码结构:在 if for 等关键词与括号之间要有一个空格;在括号内部不要有空格;在 C 允许的时候,大括号可以不写,但如果写了大括号,它们应该是这样的:
if (mro != NULL) {
    ...
}
else {
    ...
}
  • 返回语句中不要有多余的括号
return Py_None; /* correct */
return (Py_None); /* incorrect */
  • 函数或宏的调用风格 foo(a, b, c) –函数名和左括号之间没有空格,括号内部没有空格,逗号左边没有空格,每个逗号右边有一个空格
  • 总是在赋值、布尔运算符、比较运算符的两边加上空格。在有许多运算符的表达式中,在最外部(最低优先级)的运算符两边加上空格
  • 很长的行应该被折行:如果可以,在第一个参数的逗号后开始折行。总是合适地缩进剩下的行,例如:
PyErr_Format(PyExc_TypeError,
    "cannot create '%.100s' instances",
    type->tp_name);
  • 当你对一个长的表达式进行折行的时候,运算符总是在上一行的最末尾:
if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 &&
    type->tp_dictoffset == b_size &&
    (size_t)t_size == b_size + sizeof(PyObject *))
    return 0; /* "Forgive" adding a __dict__ only */
  • 在函数、结构的定义、函数中的功能块上下都加上空行
  • 先写注释,再写代码
  • 所有函数和全局变量必须被声明为 static,除非它们是发布了的或即将发布的接口
  • 对于所有外部变量和函数,我们总是在一个合适的头文件中声明它,这使用了 PyAPI_FUNC() 宏,例如:
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);

命名约定

  • 对于公共的函数,用 Py 作为前缀,这不能被用在内部函数上。而 Py_ 这个前缀是留给全局服务程序的如 Py_FatalError。而特定的程序组(某个类型的接口)会用更长的前缀,例如 PyString_ 是字符串函数的前缀。
  • 公共的函数和变量使用混合大小写以及下划线的命名规则,如 PyObject_GetAttr, Py_BuildValue, PyExc_TypeError.
  • 有时候加载器需要知道一个内部函数的名字,那这个内部函数的名字以下划线开头,如 _PyObject_Dump
  • 宏应该使用混合大小写的前缀,然后全部大写,如 PyString_AS_STRING, Py_PRINT_RAW

文档字符串

  • 使用 PyDoc_STR() 或 PyDoc_STRVAR() 这两个宏来支持构建没有文档字符串的 Python(./configure –without-doc-strings),对于那些需要支持 Python 2.3 以上的 C 代码,你可以在 h 后再 include 这个:
#ifndef PyDoc_STR
#define PyDoc_VAR(name) static char name[]
#define PyDoc_STR(str) (str)
#define PyDoc_STRVAR(name, str) PyDoc_VAR(name) = PyDoc_STR(str)
#endif
  • 每条文档字符串的第一行应该是一个对参数和返回值给出摘要的“签名”,例如:
PyDoc_STRVAR(myfunction__doc__,
"myfunction(name, value) -> bool\n\n\
Determine whether name and value make a valid pair.");

在“签名”行和下面的描述行之间总是要有空行,如果返回值总是 None(因为没有有意义的返回值),不要包括对返回值的推断。

  • 写多行文档字符串时,确保使用反斜杠,比如上面那个例子,或者是用字符串字面连接,如下:
PyDoc_STRVAR(myfunction__doc__,
"myfunction(name, value) -> bool\n\n"
"Determine whether name and value make a valid pair.");

尽管一些编译器也接受下面这种:

/*BAD--don't do this!*/
PyDoc_STRVAR(myfunction__doc__,
"myfunction(name, value)->bool\n\n
Determine whether name and value make a valid pair.");

但不是所有的编译器都接受。比如MSVC的编译器就会抱怨这种。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注