1. 程式人生 > >【譯】OpenResty C 編碼風格指南

【譯】OpenResty C 編碼風格指南

idt sym 特殊 後者 trunc ide user director 公開

OpenResty在C語言模塊遵守NGINX的編碼風格,比如OpenResty本身的NGINX插件模塊或者OpenResty的Lua庫中C部分。然而,即便是NGINX自身的C語言核心代碼風格並沒有與基礎代碼保持一致。所以,本文擬定一份指南性的文檔以便消除各種歧義。

提交給OpenResty核心項目的補丁需要遵守此指南,否則不會通過評審進程,更不用說歸並在一起。OpenRest和NGINX社區都鼓勵開發者在使用C語言開發自身模塊或庫時遵守此指南。

命名規範

對於NIGINX相關的C代碼,源碼文件名字(包括.c.h文件)、全局變量、全局函數、C語言中的結構/聯合體/枚舉的名字、編譯單元作用域的靜態變量和函數塊,還有在頭文件中公開定義的宏,以上這些都是全量命名。比如

ngx_http_core_module.c
ngx_http_finalize_request
和 NGX_HTTP_MAIN_CONF

這是十分重要的,是因為在C語言中並不像C++中有顯示的名字空間。使用全量性名字可以避免名字沖突,而且有助於調試。在Lua庫中的C組件,對於所有在相關C編譯單元中頂層C標簽中我們同樣使用前綴,比如resty_blah_(如果庫的名稱是lua-resty-blah)。

在C函數中聲明局部變量使用的短名字。在NGINX核心組件中廣泛使用短變量名字是:clevpq 等等。這些變量名通常是生命周期比較短並且是有限的作用域。根據Huffman原則,在當前上下文中對於常用的地方我們使用短變量名可以避免行噪音。但短變量名需要遵守NGINX的風格。不要自我發明除非必要,但必須使用含義明確的名字。對於p

q,它們經常用在處理字符串處理上下文中的字符串指針變量名。

一旦需要C結構體和聯合體,全寫形式命名之,除非成員的名字很長。比如,在NGINX中 struct ngx_http_request_s,有很長的成員變量名字: read_event_handlerupstream_states
request_body_in_persistent_file 等。

對於typedef指向的結構體的變量名以_t作為後綴,結構體名以_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)
{
    ...
}

註意一點的是:在 (前後都沒有空格,並且在前三行無任何縮進。

如果參數列表很長,比如超過了80個字符,以不同的行分隔之,每一行前縮進4個空格。示例:

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等。通常它們放在每個函數定義個開始處,而不是在任何代碼塊的開始處,除非有助於天使或者其他特殊的需求。同樣,函數內的標誌符(除了*前綴),必須垂直對齊。比如:

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

註意標誌符irercerrstr是如何垂直對齊,但*前綴不包括在內。

可能會出現超長的局部變量名,若是一同對齊,會導致代碼超級難看。可以將長局部變量名與其他局部變量之間插入空行分隔開來。此時,兩組標誌符,沒有必要垂直對齊。示例如下:

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函數定義、多行全局/靜態變量定義和struct/union/enum定義必須使用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;

在全局變量聲明後面使用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);

甚至在跨越多行的情況下也是如此。比如:

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

前面一組基本上是響應頭部的函數聲明,後面一組是請求體相關的函數聲明。

類型轉換

在C語言中將void指針(void *)賦值給非void指針並沒有要求顯示轉換。NGINX中的編碼風格也是如此。比如:

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

示例中conf變量是void指針類型,將其賦值給一個char *局部變量,並沒有使用任何顯示類型轉換。

確實需要顯示類型轉換的時候,確保在目標指針類型名字的第一個*前加一個空格
,同樣在)後面加入一個空格。比如:

*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 適用的if 語句與C語言中的編碼風格是一樣的。

首先,在 if後面必須有一個空格;再者(){之間有空格。如下:

if (a > 3) {
    ...
}

可以看出,在 if後面有一個空格;在 {之前有一個空格。然而在(右邊和)左邊是沒有空格。此外,
{if關鍵字在同一行,除非此行超過了80個字符。若是則,我們需要將其分隔多行,{獨立成行。
如下面的例子:

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

註意 != OKif 語句的條件部分對齊(不包括 ()。

若邏輯操作符在很長的條件語句中,需要確保連接邏輯操作符在後續行開頭處,並且縮進需體現出條件的嵌套結構。如下:

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

在代碼塊之間使用空白符,會讓代碼不那麽臃腫。同樣也適用於whilefor 等語句。

盡管只有單條件的If 語句,也需要使用 {},比如:

        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 {在同一行,並且在其上方有一空行。

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前後不一致。

全局或靜態變量

對於局部變量、頂部的靜態變量的定義和聲明,需要在類型描述符與變量名之間加入至少兩個空格(包括多個前導*修飾)

下面是一些示例:

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

操作符

二元操作符

很多二元操作符前後需要一個空格。比如,算術運算符、位運算符、關系運算符合邏輯運算符。如下:

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 定義

類似於宏,typedef定義同樣需要在定義體的前面至少有2個空格,(通常就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文件,校驗本文檔所覆蓋的風格,但不全部。`ngx-releng是OpenResty核心開發的必選工具,也有助於
NGINX模塊開發者或者NGINX核心開發者。我們會持續增加更多地校驗功能,同時我們也期待您的加入。

clang靜態代碼分析器對於獲取代碼隱匿問題是一個好幫手,你可以開啟優化配置標誌以編譯一切。

很多編輯器提供了高亮後者自動截除行尾的空白符,也可以將制表符擴展成空格。比如,在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語句使用存在恐慌,這其實是不公平的。(PS:Dijkstra大神的Go To Statement Considered Harmful,然而在《代碼大全》一書中:

90%情況下用goto都是錯的,但少數情況goto確實有效(比如在錯誤處理中的多重判斷,又如兩個條件判斷和一個else子句的情況),是解決問 題的合理辦法。這時候用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。只要校驗指針是否為NULL的地方就遵守此約定。同理,推薦使用 p != NULL而不是 p 檢驗指針的值是非NULL,但是有時候使用 p也是可以的。

下面是一些例子:

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

檢驗 NULL通常清楚的表明了檢查值屬性,並且提高代碼的可讀性。

作者

本指南由OpenResty的創建者章亦春撰寫。

反饋與建議

有任何反饋或者建議都可以發送郵件章亦春。

【譯】OpenResty C 編碼風格指南