目录
[题外话: 补充do-while(0)的大括号作用](#题外话: 补充do-while(0)的大括号作用)
1.选用的Redis版本
2025年11月18日发布的版本
https://github.com/redis/redis/releases/tag/8.4.0

源代码压缩文件下载链接: https://github.com/redis/redis/archive/refs/tags/8.4.0.zip
2.需要研究的文件
bash
redis-8.4.0/src
├──......
├── pqsort.c
├── pqsort.h
├──......
pqsort.c的前几行的注释:
cpp
/* The following is the NetBSD libc qsort implementation modified in order to
* support partial sorting of ranges for Redis.
*
* Copyright(C) 2009-current Redis Ltd.. All rights reserved.
*
* The original copyright notice follows. */
从中可以看出:pqsort从NetBSD的C库的qsort修改而来,pqsort实现的是部分范围排序(partial sorting of ranges)
下面逐行分析
3.pqsort.h
cpp
#ifndef __PQSORT_H
#define __PQSORT_H
void
pqsort(void *a, size_t n, size_t es,
int (*cmp) (const void *, const void *), size_t lrange, size_t rrange);
#endif
给出pqsort的使用方法,参数的含义大致分析:
a 应该是指向需要排序的数组吧?
n 暂不清楚
es 暂不清楚
cmp 函数指针,其指向的函数用于指定比较规则?
lrange 可能是a中需要排序部分的左边界?
rrange 可能是a中需要排序部分的右边界?
深入代码后会逐渐理解这些参数的含义
4.pqsort.c
头文件
cpp
#include <sys/types.h>
#include <stdint.h>
#include <errno.h>
#include <stdlib.h>
两个函数的声明
cpp
static inline char *med3 (char *, char *, char *,
int (*)(const void *, const void *));
static inline void swapfunc (char *, char *, size_t, int);
med3函数暂时不清楚含义,但swapfunc可以猜出应该是用于交换的函数
min宏函数
cpp
#define min(a, b) (a) < (b) ? a : b
作用: 选出a和b中最小的
swapcode宏函数
cpp
#define swapcode(TYPE, parmi, parmj, n) { \
size_t i = (n) / sizeof (TYPE); \
TYPE *pi = (TYPE *)(void *)(parmi); \
TYPE *pj = (TYPE *)(void *)(parmj); \
do { \
TYPE t = *pi; \
*pi++ = *pj; \
*pj++ = t; \
} while (--i > 0); \
}
猜测:
TYPE: s数组元素的类型
parmi,parmj: 各自指向需要交换的两个数组
n: 暂不清楚
这种写法之前在95.【C语言】解析预处理(3)文章的应用2:批量定义函数 提到过,类似C++的模版 ,显然swapcode作用是批量交换两个数组中的元素
算法:
i存储交换次数
pi,pj各自指向需要交换的两个数组
批量交换使用do-while,创建第3个临时变量用于交换
交换后,pi和pj个各向后移动一个元素的距离
那么可以推出n的含义: 数组中需要交换的元素占用的字节数,因为(n) / sizeof (TYPE)
测试代码
cpp
#include <stdio.h>
#include <stddef.h>
#define swapcode(TYPE, parmi, parmj, n) { \
size_t i = (n) / sizeof (TYPE); \
TYPE *pi = (TYPE *)(void *)(parmi); \
TYPE *pj = (TYPE *)(void *)(parmj); \
do { \
TYPE t = *pi; \
*pi++ = *pj; \
*pj++ = t; \
} while (--i > 0); \
}
int main()
{
int arr1[]={1,2,3};
int arr2[]={4,5,6};
printf("交换前:\n");
for (int i=0;i<sizeof(arr1)/sizeof(int);i++)
printf("arr1[%d]=%d arr2[%d]=%d\n",i,arr1[i],i,arr2[i]);
printf("\n");
swapcode(int,arr1,arr2,sizeof(arr1));
printf("交换后:\n");
for (int i=0;i<sizeof(arr1)/sizeof(int);i++)
printf("arr1[%d]=%d arr2[%d]=%d\n",i,arr1[i],i,arr2[i]);
return 0;
}
运行结果

从预处理看看动态生成的swapcode函数
cpp
#define swapcode(TYPE, parmi, parmj, n) { \
size_t i = (n) / sizeof (TYPE); \
TYPE *pi = (TYPE *)(void *)(parmi); \
TYPE *pj = (TYPE *)(void *)(parmj); \
do { \
TYPE t = *pi; \
*pi++ = *pj; \
*pj++ = t; \
} while (--i > 0); \
}
int main()
{
int arr1[]={1,2,3};
int arr2[]={4,5,6};
swapcode(int,arr1,arr2,sizeof(arr1));
char arr3[]={'a','b','c'};
char arr4[]={'d','e','f'};
swapcode(char,arr3,arr4,sizeof(arr3));
return 0;
}
让gcc只执行预处理这一步,编译以上代码:
bash
gcc -E ./test_redis_swap.c -o ./test_redis_swap.i
运行结果
swapcode(int,arr1,arr2,sizeof(arr1))和swapcode(char,arr3,arr4,sizeof(arr3))被gcc展开,生成的test_redis_swap.i为:
cpp
# 0 "./test_redis_swap.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "./test_redis_swap.c"
# 12 "./test_redis_swap.c"
int main()
{
int arr1[]={1,2,3};
int arr2[]={4,5,6};
{ size_t i = (sizeof(arr1)) / sizeof (int); int *pi = (int *)(void *)(arr1); int *pj = (int *)(void *)(arr2); do { int t = *pi; *pi++ = *pj; *pj++ = t; } while (--i > 0); };
char arr3[]={'a','b','c'};
char arr4[]={'d','e','f'};
{ size_t i = (sizeof(arr3)) / sizeof (char); char *pi = (char *)(void *)(arr3); char *pj = (char *)(void *)(arr4); do { char t = *pi; *pi++ = *pj; *pj++ = t; } while (--i > 0); };
return 0;
}
注意swapcode细节
宏函数最外侧有花括号,这是不可省略的!
整体上swapcode宏函数的写法:
cpp
#define swapcode(...) { ... }
细节: 替换后,外层有花括号
如果去掉花括号,编译以下代码:
cpp
#include <stddef.h>
//省略swapcode的定义
int main()
{
int arr1[]={1,2,3};
int arr2[]={4,5,6};
int pi=0;
int pj=0;
swapcode(int,arr1,arr2,sizeof(arr1));
return 0;
}
gcc直接报错:

自己定义的pi、pj是int类型,而宏替换后pi、pj又被定义为int*类型,于是gcc报类型冲突(conflicting types)的问题,而加上花括号不存在这样的问题
题外话: 补充do-while(0)的大括号作用
swapcode中用到了do-while,这让我想起了do-while在宏中能充当大括号作用,例如linux 6.18.4内核的/include/linux/printk.h中这样一段代码:
cpp
/**
* printk_cpu_sync_put_irqrestore() - Release the printk cpu-reentrant spinning
* lock and restore interrupts.
* @flags: Caller's saved interrupt state, from printk_cpu_sync_get_irqsave().
*/
#define printk_cpu_sync_put_irqrestore(flags) \
do { \
__printk_cpu_sync_put(); \
local_irq_restore(flags); \
} while (0)
这里不需要理解printk_cpu_sync_put_irqrestore具体是干什么的,只需要知道do {...}while(0)这个结构,其能充当大括号使用
例题
设计一个my_marco_func()宏函数,执行以下代码:
cpp
int main()
{
if (1)
my_marco_func();
else
printf("333\n");
}
注意: do-while循环在C语言中必须以分号结束
要求打印结果为:

代码1(错误)
可能有读者会这样想: 宏函数一次性需要执行两个printf,直接用花括号不就行了嘛
cpp
#include <stdio.h>
#define my_marco_func() { \
printf("111\n"); \
printf("222\n"); \
}
int main()
{
if (1)
my_marco_func();
else
printf("333\n");
}
但这样会引发一个语法错误: 悬垂else,也叫悬空else (讲解在21.【C语言】顺序结构和选择结构之if文章)
gcc报错如下:

看看预处理文件:

那把my_marco_func()结尾的;去除不就行了?
cpp
if (1)
my_marco_func() //去除;
else
printf("333\n");
运行结果:虽然可以,但不建议去除,这样不符合函数调用的习惯

代码2(错误)
如果直接替换为printf,而不加花括号,也会引发悬垂else的问题,这里省略分析
cpp
#include <stdio.h>
#define my_marco_func() { \
printf("111\n"); \
printf("222\n");
int main()
{
if (1)
my_marco_func()
else
printf("333\n");
}
运行结果:

代码3(正确)
cpp
#include <stdio.h>
#define my_marco_func() do \
{ \
printf("111\n"); \
printf("222\n"); \
} \
while (0);
int main()
{
if (1)
my_marco_func();
else
printf("333\n");
}
运行结果:

看看预处理文件:

剩余部分代码,下一篇文章再分析