OpenResty® 通过 Lua 扩展 NGINX 的可扩展 Web 平台

OpenResty XRay

为 OpenResty 及其他构建的先进可观察性

×

限时优惠

申请 试用版 今天并获取诊断 报告
Learn more
最新! OpenResty 1.25.3.2 已发布!
最新! OpenResty 1.21.4.4 已发布!
最新! 新的博客文章 OpenResty XRay 移动应用简介 已发布。
最新! 新的博客文章 为 OpenResty Edge 中的网关服务器启用自动健康检查 已发布。

OpenResty® C 代码风格指南

章亦春 (agentzh) , 2019 年 5 月 14 日 (创建于 2019 年 2 月 11 日)

OpenResty 在其 C 语言组件中遵循 NGINX 的代码风格,例如 OpenResty 自有的 NGINX 附加模块和 OpenResty 自有的 Lua 库的 C 部分。不幸的是,即使 NGINX 内核自身的 C 源代码可能也没有严格遵循与代码库其余部分相同的约定。因此,需要准备一份正式的指南文档,以避免任何歧义。

贡献给 OpenResty 核心项目的补丁应该始终遵循本指南,否则它们将无法通过审核流程,也不会以原样合并。鼓励 OpenResty 和 NGINX 社区在使用 C 开发自己的附加模块和库时始终遵循本指南。

命名约定

对于 NGINX 相关的 C 代码,源文件名(包括 .c.h 文件)、全局变量、全局函数、C 结构体/联合体/枚举名称、编译单元范围内的静态变量和函数,以及在头文件中定义的公共宏,应该始终使用全限定名称,例如 ngx_http_core_module.cngx_http_finalize_requestNGX_HTTP_MAIN_CONF。这很重要,因为 C 语言没有像 C++ 那样有显式命名空间的概念。使用全限定名称有助于避免符号冲突,也有助于调试。

在 Lua 库的 C 组件中,我们也应该为相应 C 编译单元中所有顶层 C 符号使用前缀,例如 resty_blah_(如果库名为 lua-resty-blah)。

我们应该为在 C 函数中定义的局部变量使用短名称。在 NGINX 内核中广泛使用的常见短名称有 clevctxvpq 等。这些变量通常是短暂的,并且具有非常有限的范围。根据霍夫曼原理,我们应该为当前上下文中常用的东西使用短名称,以避免代码行噪音。即使是短名称也应该遵循 NGINX 的约定。除非必要,不要发明自己的名称。并且要使用有意义的名称。即使对于 pq,它们也是在字符串处理上下文中使用的字符串指针变量的常用名称。

C 结构体和联合体名称应尽可能使用单词的全拼形式(除非成员名称太长)。例如,在 NGINX 内核的 struct ngx_http_request_s 中,我们有长成员名称,如 read_event_handlerupstream_statesrequest_body_in_persistent_file

我们应该为引用结构体的 typedef 类型名称使用 _t 后缀,为 struct 名称使用 _s,为引用枚举的 typedef 类型名称使用 _e。在函数范围内定义的局部类型不受此后缀约定的约束。以下是来自 NGINX 内核的一些示例

typedef struct ngx_connection_s      ngx_connection_t;
typedef struct {
    WSAOVERLAPPED    ovlp;
    ngx_event_t     *event;
    int              error;
} ngx_event_ovlp_t;
struct ngx_chain_s {
    ngx_buf_t    *buf;
    ngx_chain_t  *next;
};
typedef enum {
    ngx_pop3_start = 0,
    ngx_pop3_user,
    ...
    ngx_pop3_auth_external
} ngx_pop3_state_e;

缩进

NGINX 世界专门使用空格进行缩进。不要使用制表符!通常我们使用4 个空格的缩进,除非在某些情况下存在一些特殊的对齐要求或其他要求(我们将在下面详细解释这些情况)。

始终正确缩进代码。

80 列限制

所有源代码行都应保持在 80 列限制内(NGINX 内核中的一些代码甚至保持在 78 列,但我建议将 80 列作为硬限制)。不同的上下文将对续行使用的缩进有不同的缩进规则。我们将在下面详细讨论每种情况。

行尾空白

源代码行末不应该有任何空格或制表符,即使是空白行也不应该有。许多编辑器支持自动突出显示或修剪此类空白字符。确保正确配置您的编辑器或 IDE。

函数声明

在头文件或 .c 文件开头使用的 C 函数声明(不是定义!)应尽可能将所有内容放在一行中。以下是一个来自 NGINX 内核的示例

ngx_int_t ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags);

如果该行太长,超过 80 列,那么我们应该将声明分成多行,每行缩进 4 个空格。例如,

ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
    ngx_module_t *m, ngx_int_t error);

如果返回类型是指针类型,那么在第一个 * 之前应该有一个空格,但之后不应该有空格,如

char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

请注意,函数定义遵循与声明不同的风格。有关更多详细信息,请参阅 函数定义

函数定义

C 函数定义遵循与声明不同的风格(请参阅 函数声明)。第一行应该是单独的返回类型,第二行应该是函数名称以及参数列表,第三行应该是单独的左大括号。以下是一个来自 NGINX 内核的示例

ngx_int_t
ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)
{
    ...
}

请注意,参数列表的 ( 字符周围没有空格。并且前 3 行没有缩进。

如果参数列表太长,例如超过 80 列限制,那么我们可以将参数列表分成单独的行,每行缩进 4 个空格。以下是一个来自 NGINX 内核的示例

ngx_int_t
ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,
    ngx_str_t *value)
{
    ...
}

如果返回类型是指针类型,那么在第一个 * 之前应该有一个空格,例如

static char *
ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data)
{
    ...
}

局部变量

命名约定 部分,我们要求局部变量使用更短的名称,例如 evclcf 等。它们的定义也有一些风格要求。

它们应该始终放在每个 C 函数定义块的开头,而不仅仅是放在任何任意代码块的开头,除非为了帮助调试或一些特殊要求。此外,它们的变量标识符(不包括任何 * 前缀)必须垂直对齐。以下是一个来自 NGINX 内核的示例

    ngx_str_t            *value;
    ngx_uint_t            i;
    ngx_regex_elt_t      *re;
    ngx_regex_compile_t   rc;
    u_char                errstr[NGX_MAX_CONF_ERRSTR];

请注意 valueirercerrstr 标识符是如何垂直对齐的。* 前缀不计入此对齐。

有时,一些局部变量的定义可能异常长,与其他变量对齐可能会使代码变得难看。那么我们应该在这个长变量定义和其余局部变量定义之间放置一个空行。在这种情况下,这两个组的标识符不需要垂直对齐。以下是一个这样的示例

static char *
ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf = conf;

    time_t       inactive;
    ngx_str_t   *value, s;
    ngx_int_t    max;
    ngx_uint_t   i;
    ...
}

请注意 clcf 变量的定义是如何与其余局部变量分开的,中间有一个空行。其余局部变量仍然垂直对齐。

局部变量声明也必须后跟一个空行,用于将它们与当前 C 函数的实际执行代码语句分开。例如

u_char * ngx_cdecl
ngx_sprintf(u_char *buf, const char *fmt, ...)
{
    u_char   *p;
    va_list   args;

    va_start(args, fmt);
    p = ngx_vslprintf(buf, (void *) -1, fmt, args);
    va_end(args);

    return p;
}

在局部变量定义之后有一个空行。

空行的使用

连续的 C 函数定义、多行全局/静态变量定义和结构体/联合体/枚举定义必须用 2 个空行隔开。以下是连续 C 函数定义的示例

void
foo(void)
{
    /* ... */
}


int
bar(...)
{
    /* ... */
}

以下是如何定义连续的静态变量

static ngx_conf_bitmask_t  ngx_http_core_keepalive_disable[] = {
    ...
    { ngx_null_string, 0 }
};


static ngx_path_init_t  ngx_http_client_temp_path = {
    ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 }
};

单行变量定义可以分组,例如

static ngx_str_t  ngx_http_gzip_no_cache = ngx_string("no-cache");
static ngx_str_t  ngx_http_gzip_no_store = ngx_string("no-store");
static ngx_str_t  ngx_http_gzip_private = ngx_string("private");

以下是连续的(多行)结构体定义的示例

struct ngx_http_log_ctx_s {
    ngx_connection_t    *connection;
    ngx_http_request_t  *request;
    ngx_http_request_t  *current_request;
};


struct ngx_http_chunked_s {
    ngx_uint_t           state;
    off_t                size;
    off_t                length;
};


typedef struct {
    ngx_uint_t           http_version;
    ngx_uint_t           code;
    ngx_uint_t           count;
    u_char              *start;
    u_char              *end;
} ngx_http_status_t;

所有这些都用 2 个空行隔开。

如果这些顶层对象定义的不同类型是相邻的,则它们也应该用 2 个空行隔开,例如

#if (NGX_HTTP_DEGRADATION)
ngx_uint_t  ngx_http_degraded(ngx_http_request_t *);
#endif


extern ngx_module_t  ngx_http_module;

静态函数声明与后面的 C 全局变量声明之间用 2 个空行隔开。

连续的 C 函数声明不需要用 2 个空行隔开,例如

ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
void ngx_http_discarded_request_body_handler(ngx_http_request_t *r);
void ngx_http_block_reading(ngx_http_request_t *r);
void ngx_http_test_reading(ngx_http_request_t *r);

即使其中一些跨越多行,例如

char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys,
    ngx_hash_t *types_hash, ngx_array_t **prev_keys,
    ngx_hash_t *prev_types_hash, ngx_str_t *default_types);
ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
    ngx_str_t *default_type);

不过,有时为了提高代码的可读性,我们可以用 2 个空行将它们分成有语义意义的组,例如

ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
    ngx_int_t error);
ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
    ngx_module_t *m, ngx_int_t error);
void ngx_http_clean_header(ngx_http_request_t *r);


ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
void ngx_http_discarded_request_body_handler(ngx_http_request_t *r);
void ngx_http_block_reading(ngx_http_request_t *r);
void ngx_http_test_reading(ngx_http_request_t *r);

第一组主要与响应头有关,而第二组与请求体有关。

类型转换

在将 void 指针 (void *) 的值赋值给非 void 指针时,C 语言不需要显式类型转换。NGINX 编码风格也不需要这样做。例如

char *
ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;
    ...
}

这里 conf 变量是一个 void 指针,NGINX 内核将它赋值给类型为 char * 的局部变量 p,而没有进行任何显式类型转换。

如果需要显式类型转换,请确保在目标指针类型名称的第一个 * 字符前有一个空格,并在 ) 字符后也有一个空格,例如

*types = (void *) -1;

*) 前有一个空格,在 ) 后也有一个空格。这同样适用于需要类型转换的值是一个示例的情况

if ((size_t) (last - buf) < len) {
    ...
}

或者多个连续的类型转换

aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev;

注意 (uint64_t)(uintptr_t) 之间的空格,以及 (uintptr_t) 后的空格。

If 语句

NGINX 使用 C 的 if 语句也有一些风格要求。

首先,if 关键字后必须有一个空格,条件的右括号和左花括号之间也必须有一个空格。也就是说

if (a > 3) {
    ...
}

注意 if 后的空格和 { 之前的空格。但是请注意,( 后和 ) 前没有空格。

还要注意,左花括号必须与 if 关键字位于同一行,除非该行超过 80 列,在这种情况下,我们应该将条件分成多行并将左花括号放在单独的一行。以下示例演示了这一点

            if (ngx_http_set_default_types(cf, prev_keys, default_types)
                != NGX_OK)
            {
                return NGX_CONF_ERROR;
            }

注意 != OK 如何与 if 语句的条件部分(不包括 ()垂直对齐。

当长条件部分涉及逻辑运算符时,我们应该确保连接的逻辑运算符位于后续行的开头,并且缩进反映条件表达式的嵌套结构,例如

        if (file->use_event
            || (file->event == NULL
                && (of->uniq == 0 || of->uniq == file->uniq)
                && now - file->created < of->valid
#if (NGX_HAVE_OPENAT)
                && of->disable_symlinks == file->disable_symlinks
                && of->disable_symlinks_from == file->disable_symlinks_from
#endif
            ))
        {
            ...
        }

我们可以忽略中间的宏指令。它们与 if 语句本身的编码风格无关。

通常,如果 if 语句的代码块后面还有其他语句,我们应该在代码块之后留一个空行。例如

        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
            goto failed;
        }

        if (of->is_dir) {
            ...
        }

注意如何使用空行来分隔连续的 if 语句块。或者和其他语句

        if (file->is_dir) {

            /*
             * chances that directory became file are very small
             * so test_dir flag allows to use a single syscall
             * in ngx_file_info() instead of three syscalls
             */

            of->test_dir = 1;
        }

        of->fd = file->fd;
        of->uniq = file->uniq;

同样地,通常在 if 语句之前使用一个空行,例如

        rc = ngx_open_and_stat_file(name, of, pool->log);

        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
            goto failed;
        }

在这些代码块周围使用空行有助于使代码不那么拥挤。同样适用于 “while” 语句、for 语句等。

If 语句必须始终使用花括号,即使 “then” 分支只有一个语句。例如

            if (file->is_dir || file->err) {
                goto update;
            }

即使标准 C 语言允许这样做,我们也不应该省略这些花括号。

else 部分

if 语句包含 else 分支时,它也必须使用花括号来分组包含的语句。此外,在 } else { 行之前必须使用一个空行。以下是一个示例

    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER
        && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))
    {
        fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);

    } else {
        fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);
    }

注意 } else { 如何放在同一行,并且 } else { 行之前有一个空行。

For 语句

for 语句在许多方面与 If 语句 部分介绍的 if 语句风格类似。for 关键字之后也需要一个空格,{ 之前也需要一个空格。此外,必须使用花括号来包含语句。此外,在 for 条件部分的 ; 后需要一个空格。以下示例演示了这些要求

for (i = 0; i < size; i++) {
    ...
}

特殊情况是无限循环,在 NGINX 世界中通常以下面的方式编码

    for ( ;; ) {
        ...
    }

或者在 for 语句的条件部分使用逗号表达式时

    for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
        ...
    }

或者当循环条件本身被省略时

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ...
    }

While 语句

while 语句在许多方面与 If 语句 部分介绍的 if 语句风格类似。while 关键字之后也需要一个空格,{ 之前也需要一个空格。此外,必须使用花括号来包含语句。以下是一个示例

    while (log->next) {
        if (new_log->log_level > log->next->log_level) {
            new_log->next = log->next;
            log->next = new_log;
            return;
        }

        log = log->next;
    }

Do-while 语句也类似

        do {
            p = h2c->state.handler(h2c, p, end);

            if (p == NULL) {
                return;
            }

        } while (p != end);

注意 do{ 之间使用一个空格,以及 while 前后的空格。

Switch 语句

switch 语句在许多方面与 If 语句 部分介绍的 if 语句风格类似。switch 关键字之后也需要一个空格,{ 之前也需要一个空格。此外,必须使用花括号来包含语句。以下是一个示例

    switch (unit) {
    case 'K':
    case 'k':
        len--;
        max = NGX_MAX_SIZE_T_VALUE / 1024;
        scale = 1024;
        break;

    case 'M':
    case 'm':
        len--;
        max = NGX_MAX_SIZE_T_VALUE / (1024 * 1024);
        scale = 1024 * 1024;
        break;

    default:
        max = NGX_MAX_SIZE_T_VALUE;
        scale = 1;
    }

注意 case 标签如何与 switch 关键字垂直对齐。

有时,在第一个 case 标签行之前使用一个空行,例如

        switch (c->log_error) {

        case NGX_ERROR_IGNORE_EINVAL:
        case NGX_ERROR_IGNORE_ECONNRESET:
        case NGX_ERROR_INFO:
            level = NGX_LOG_INFO;
            break;

        default:
            level = NGX_LOG_ERR;
        }

分配错误处理

NGINX 世界有一个很好的习惯,始终检查动态内存分配失败。它无处不在,例如

    sa = ngx_palloc(cf->pool, socklen);
    if (sa == NULL) {
        return NULL;
    }

这两个语句经常一起出现,因此我们通常不会在分配语句和 if 语句之间放置空行。

确保在动态内存分配语句后,您永远不会省略此检查。

函数调用

C 函数调用不应在参数列表的左括号或右括号周围放置任何空格。以下是一个示例

sa = ngx_palloc(cf->pool, socklen);

当函数调用很长,超过 80 列限制时,我们应该将参数列表分成单独的行。后续行必须与第一个参数垂直对齐,例如

        buf->pos = ngx_slprintf(buf->start, buf->end, "MEMLOG %uz %V:%ui%N",
                                size, &cf->conf_file->file.name,
                                cf->conf_file->line);

宏定义需要在 #define 后面有一个空格,而在定义主体部分之前至少要有 2 个空格。例如

#define F(x, y, z)  ((z) ^ ((x) & ((y) ^ (z))))

有时为了使多个紧密相关的宏定义垂直对齐,在定义主体部分之前可能会使用更多的空格,例如

#define NGX_RESOLVE_A         1
#define NGX_RESOLVE_CNAME     5
#define NGX_RESOLVE_PTR       12
#define NGX_RESOLVE_MX        15
#define NGX_RESOLVE_TXT       16
#define NGX_RESOLVE_AAAA      28
#define NGX_RESOLVE_SRV       33
#define NGX_RESOLVE_DNAME     39
#define NGX_RESOLVE_FORMERR   1
#define NGX_RESOLVE_SERVFAIL  2

对于跨越多行的宏定义,我们应该将行延续字符 \ 垂直对齐,例如

#define ngx_conf_init_value(conf, default)
\
    if (conf == NGX_CONF_UNSET) {                                            \
        conf = default;                                                      \
    }

我们建议将 \ 放在第 78 列,尽管 NGINX 内核有时会与自身不一致。

全局/静态变量

全局变量、静态变量和顶层静态变量的定义和声明,类型声明符和变量标识符部分(包括任何前导的*修饰符)之间应该至少有 2 个空格。以下是一些示例

ngx_uint_t   ngx_http_max_module;


ngx_http_output_header_filter_pt  ngx_http_top_header_filter;
ngx_http_output_body_filter_pt    ngx_http_top_body_filter;
ngx_http_request_body_filter_pt   ngx_http_top_request_body_filter;

对于带有初始化表达式的变量定义,也适用此规则,例如

ngx_str_t  ngx_http_html_default_types[] = {
    ngx_string("text/html"),
    ngx_null_string
};

运算符

二元运算符

大多数二元 C 运算符(如算术运算符、位运算符、关系运算符和逻辑运算符)之前和之后都需要一个空格。以下是一些示例

 yday = days - (365 * year + year / 4 - year / 100 + year / 400);

以及

if (*p >= '0' && *p <= '9') {

对于结构体/联合体成员运算符->.,不允许在它们周围使用空格,例如

ls = cycle->listening.elts;

对于逗号运算符,逗号之后应该使用一个空格,而不是之前

for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {

NGINX 通常避免使用逗号运算符,除非在for语句条件和相同类型的多个变量声明中。在其他情况下,最好将逗号表达式拆分为单独的语句。

一元运算符

我们通常不在 C 一元前缀运算符之前或之后添加空格。以下是一些示例

for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ }
#define SET(n)      (*(uint32_t *) &p[n * 4])

注意,我们不会在一元*运算符或一元&运算符周围添加空格(上面第二个示例中&之前的空格是由于使用了类型转换表达式;有关更多详细信息,请参阅类型转换部分)。

后缀运算符也适用此规则

for (value = 0; n--; line++) {

三元运算符

三元运算符也需要在运算符周围使用空格,就像二元运算符一样。例如

node = (rc < 0) ? node->left : node->right;

从这个例子中可以看出,当三元运算符的条件部分是一个表达式时,我们可以在它周围添加一对括号。不过这不是必需的。

结构体/联合体/枚举定义

结构体、联合体和枚举的定义风格类似。它们应该将字段的标识符垂直对齐,类似于局部变量部分中介绍的局部变量定义。我们只给出 NGINX 核心的一些实际示例来演示这种风格

typedef struct {
    ngx_uint_t           http_version;
    ngx_uint_t           code;
    ngx_uint_t           count;
    u_char              *start;
    u_char              *end;
} ngx_http_status_t;

就像局部变量定义一样,我们也可以使用空行来分隔字段组,例如

struct ngx_http_request_s {
    uint32_t                          signature;         /* "HTTP" */

    ngx_connection_t                 *connection;

    void                            **ctx;
    void                            **main_conf;
    void                            **srv_conf;
    void                            **loc_conf;

    ngx_http_event_handler_pt         read_event_handler;
    ngx_http_event_handler_pt         write_event_handler;
    ...
};

在这种情况下,每组仍然必须将字段成员标识符垂直对齐,但不同的组不需要对齐(尽管我们仍然可以对齐,如上面的示例所示)。

联合体类似

typedef union epoll_data {
    void         *ptr;
    int           fd;
    uint32_t      u32;
    uint64_t      u64;
} epoll_data_t

枚举也是如此

typedef enum {
    NGX_HTTP_INITING_REQUEST_STATE = 0,
    NGX_HTTP_READING_REQUEST_STATE,
    NGX_HTTP_PROCESS_REQUEST_STATE,

    NGX_HTTP_CONNECT_UPSTREAM_STATE,
    NGX_HTTP_WRITING_UPSTREAM_STATE,
    NGX_HTTP_READING_UPSTREAM_STATE,

    NGX_HTTP_WRITING_REQUEST_STATE,
    NGX_HTTP_LINGERING_CLOSE_STATE,
    NGX_HTTP_KEEPALIVE_STATE
} ngx_http_state_e;

类型定义

类似,typedef定义也需要在定义体部分之前至少有 2 个空格(通常只有 2 个)。例如,

typedef u_int  aio_context_t;

当将一组类型定义放在一起时,可以使用超过 2 个空格,并且为了美观原因,将其垂直对齐也是不错的选择,例如

typedef struct ngx_module_s          ngx_module_t;
typedef struct ngx_conf_s            ngx_conf_t;
typedef struct ngx_cycle_s           ngx_cycle_t;
typedef struct ngx_pool_s            ngx_pool_t;
typedef struct ngx_chain_s           ngx_chain_t;
typedef struct ngx_log_s             ngx_log_t;
typedef struct ngx_open_file_s       ngx_open_file_t;

工具

OpenResty 团队维护着ngx-releng工具,用于静态扫描当前 C 源代码树以查找许多(但并非全部)本文档中介绍的风格问题。对于 OpenResty 核心开发人员来说,它是一个必不可少的工具,对于 NGINX 模块开发人员和 NGINX 核心黑客来说也很有用。我们一直在向该工具添加更多检查器,也欢迎您的贡献。

clang 静态代码分析器对于发现细微的编码问题也很有用,使用 gcc 的高级优化标志编译所有内容也是如此。

许多编辑器提供了突出显示和/或自动修剪行尾空格以及将制表符扩展为空格的功能。例如,在 vim 中,我们可以在~/.vimrc文件中添加以下行来突出显示任何行尾空格

highlight WhiteSpaceEOL ctermbg=darkgreen guibg=lightgreen
match WhiteSpaceEOL /\s$/
autocmd WinEnter * match WhiteSpaceEOL /\s$/

并设置缩进功能

set expandtab
set shiftwidth=4
set softtabstop=4
set tabstop=4

goto 语句和代码标签

NGINX 明智地使用goto语句进行错误处理。对于臭名昭著的goto语句来说,这是一个很好的用例。许多没有经验的 C 程序员可能会对任何使用goto语句的行为感到恐慌,这是不公平的。仅仅使用goto语句进行向后跳转是不好的,否则通常是可以的,特别是对于错误处理。NGINX 要求代码标签用空行包围,例如

        p = ngx_pnalloc(pool, len);
        if (p == NULL) {
            goto failed;
        }

        ...

        i++;
    }

    freeaddrinfo(res);
    return NGX_OK;

failed:

    freeaddrinfo(res);
    return NGX_ERROR;

检查指针空值

在 NGINX 世界中,我们通常使用p == NULL而不是!p来检查指针值是否为NULL。尽可能遵循此约定。也建议使用p != NULL而不是p来测试相反的情况,但在这种情况下,简单地使用p进行测试也是可以的。

以下是一些示例

if (addrs != NULL) {
if (name == NULL) {

NULL进行比较通常可以更清楚地说明被检查值的性质,从而有助于提高代码可读性。

作者

本指南的作者是张逸飞,OpenResty 的创建者。

反馈和补丁

欢迎提供反馈和补丁!请将它们发送到张逸飞的电子邮件地址yichun@openresty.com