记录 C 语言相关小知识。

1. 关键字

1.1. volatile

1
volatile unsigned long lru_clock; /* Server global current LRU time. */

参考 C 语言标准谈谈 C/C++ 中的 volatile 可知:对 volatile 变量的访问,编译器不能做任何假设和推理,都必须按部就班地与「内存」进行交互。

2. 结构体

2.1. 结构体内的冒号

1
2
3
4
5
6
7
typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:24;
    int refcount;
    void *ptr;
} robj;

用冒号限定该 field 所占的位数。

3. 编译器相关

3.1. __builtin_expect

GCC (version >= 2.96) 提供 __builtin_expect() 的支持,允许程序员将最有可能执行的分支告诉编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

一般的使用方法是使用 likelyunlikely 封装该指令,例如:

1
2
3
4
5
6
7
#if __GNUC__ >= 3
#define likely(x) __builtin_expect(!!(x), 1)    /* x 很可能为真 */
#define unlikely(x) __builtin_expect(!!(x), 0)  /* x 很可能为假 */
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif

此处两次取非的作用是将 x 强转为“布尔类型”(0 or 1 in C)。

4. 字符串

4.1. memcmp

通常我们会使用 memcmp 比较两个字符串:

1
int memcmp( const void* lhs, const void* rhs, size_t count );

但需要注意的是 memcmp 可能不会在字符不相同时立马返回,也就是对于 lhs/rhs 来说,需保证字符串长度不小于 count ,否则会产生未定义行为,比如 redis/pull/7443

详情可阅读 memcmp-requires-pointers-to-fully-valid-buffers.

5. Utils

5.1. random

  • 在使用 random 函数获取随机数时需注意其返回值值域 —— long int 为 4 字节长。
  • 一般情况下,这不会有什么问题,但需考虑到极端情况的存在,比如 redis/pull/8133.
1
extern long int random (void) __THROW;