序言

译者:这份指南是在阅读 OpenResty® C Coding Style Guide 文章后翻译的。借此机会学习与理解英语、NGINX 编码风格。

OpenResty 在它的 C 语言组件中遵循 NGINX 的编码风格,如 OpenResty 自己的 NGINX 模块和 Lua 库的 C 语言部分。遗憾的是,即使是 NGINX 自己的核心 C 源码也没能严格的遵守与其他代码库相同的编码风格。所以希望准备一份正式指导手册,以避免产生歧义。

贡献给 OpenResty 核心项目的补丁应始终遵循这份指南,不然它将无法通过代码检查,也无法按原样合并。在使用 C 开发自己的模块和库时,OpenResty 和 NGINX 社区始终鼓励遵循这份指南。

命名约定

查看英文原文

NGINX 源代码中的命名规则应该始终使用完整名称,包括源码文件名(.c, .h)、全局变量、全局函数、C 结构体/联合/枚举、静态变量/函数以及 .h 头文件内的公共宏定义。这一点很重要,因为 C 没有像 C++ 中那样的显式名称空间的概念。使用完整名称有助于调试,避免符号冲突。例如,ngx_http_core_module.c, ngx_http_finalize_request, NGX_HTTP_MAIN_CONF

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

我们在 C 函数中定义局部变量的时候应该使用短名称。在 NGINX 核心代码中有大量常用的短名称,比如,cl, ev, ctx, v, p, q 等等。这些变量一般都是临时的、局部的。根据霍夫曼原则,我们应该在当前上下文环境中使用通用的短名称来避免单行干扰。即使是短名称也应该遵循 NGINX 的约定。不要自己创造新的规则,如果需要,且使用有意义的名称。即使是 pq,它们也是字符串指针变量的常用名称,在字符串处理上下文环境当中使用。

C 语言结构体和联合名称尽可能的使用单词的完整拼写形式(除非成员名称太长)。例如,NGINX 核心结构体 ngx_http_request_s 当中有像 read_event_handler, upstream_states, request_body_in_persistent_file 这样很长的成员名称。

我们应该在 typedef 类型名称时使用 _t 结尾,定义结构体名称时使用 _s 结尾,定义枚举类型时使用 _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)
{
    ...
}

注意:参数列表 ( 字符周围没有空格,并且这三行是没有缩进的。

如果参数列表太长,超过 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)
{
    ...
}

局部变量

查看英文原文

命名约定中要求局部变量使用更短的名称,类似 ev, clcf 这样的。它们在定义的时候也有一些要求。

它们应总是写在每个 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];

请注意 value, i, re, rc, errstr 这些标识符是如何垂直对其的。前缀 * 不计入对其。

有的时候,某些局部变量的定义可能特别长,要与其余变量对其可能会使代码变得丑陋。然后我们在它们之间放一个空行。 在这种情况下,两组定义是不需要垂直对齐的。以下就是这样的例子:

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 函数声明无需如此,例如:

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

即便有些 C 函数声明跨多行也是如此,例如:

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 语句 章节中的内容类似。 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 语句章节中的内容类似。 在 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 语句章节中的内容类似。在 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 语句之间加空行。

确保不要省略动态内存分配语句后的 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 语句条件上下文、多个类型相同的变量声明当中使用逗号。而在其他情况下,最好将逗号表达式拆分成独立的语句。

一元运算符

通常在前缀一元运算符的前后是不加空格的。以下是些例子:

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 个空格符。例如:

typedef u_int  aio_context_t;

在把一组 typedef 定义放在一起时,需 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 要求 goto 标签行前后加空行,如

        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 Zhang。

反馈与补丁

随时欢迎反馈和补丁!它们应提交至 Yichun Zhang 的电子邮件地址 yichun@openresty.com

参考链接