人类吵了几千年的问题,我用 qsort 裁决了。完整代码如下:
先有鸡还是先有蛋.c1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <stdio.h> #include <stdlib.h> #include <string.h>
int number[] = {3, 2, 1}; char letter[] = {'l', 'e', 'j'}; char* object[] = {"🐔", "🥚"};
int _intcmp_(const void* a,const void* b) { return *(int*)a > *(int*)b; }
int _charcmp_(const void* a,const void* b) { return *(char*)a > *(char*)b; }
int _strcmp_(const void *a, const void *b) { return strcmp((char*)a, (char*)b); }
int main () {
printf("before:%d %d %d\n", number[0], number[1], number[2]); qsort(number, 3, sizeof(int), _intcmp_); printf("after:%d %d %d\n", number[0], number[1], number[2]);
printf("before:%c %c %c\n", letter[0], letter[1], letter[2]); qsort(letter, 3, sizeof(char), _charcmp_); printf("after:%c %c %c\n", letter[0], letter[1], letter[2]);
printf("before:%s %s\n", object[0], object[1]); qsort(object, 2, sizeof(char*), _strcmp_); printf("after:%s %s\n", object[0], object[1]); getchar(); return 0; }
|
三个数组,三种类型,同一个 qsort,三个自定义比较函数。标题的问题就藏在第三个数组里——object[] 的两个元素,一个是 🐔,一个是 🥚。排序完,谁在前?
真机运行
1 2 3 4 5 6
| before:3 2 1 after:1 2 3 before:l e j after:e j l before:🐔 🥚 after:🐔 🥚
|
数字归位(3 2 1 → 1 2 3),字母归位(l e j → e j l,按 ASCII:e 是 101、j 是 106、l 是 108)。然后是万众瞩目的判决:🐔 🥚 排序前后纹丝不动——qsort 用 UTF-8 字节序庄严宣判:先有鸡。
为什么emoji能比大小
strcmp 逐字节比较。emoji 在 UTF-8 里是 4 个字节:🐔 是 F0 9F 8C 94,🥚 是 F0 9F A5 9A。逐字节比到第三个字节,8C < A5 分出胜负——鸡以一个字节的微弱优势战胜了蛋。
同一个程序里其实演了三代”字符”:int 按二进制比、char 按 ASCII 比、emoji 按 UTF-8 字节比——比较函数变了三次,”谁在前”的规则就变了三次。所谓排序结果,从来不是客观事实,而是比较函数的投影。
顺带一提被注释掉的那行 SetConsoleOutputCP(65001):老 Windows 控制台默认代码页显示不了 emoji,要切到 UTF-8 代码页才能看见这两只小可爱——所以当年的运行截图里,能不能看到鸡和蛋,取决于有没有解开这行注释。
正经话
这也是一堂微型的 qsort 教学课:四个参数(数组、元素个数、单元素大小、比较函数)各自的职责;比较函数的两种”偷懒”写法——数值直接比大小、字符串直接 strcmp 返回值——都恰好在”升序”这个需求下成立。以及那个最容易被忽略的细节:第三个数组比较的是 char*,所以 sizeof(char*) 才是单元素大小——比的是指针,不是鸡。
人类争了几千年的哲学问题,在 C 语言里被压缩成一次字节比较。当然,这只是编码顺序的判决,不是哲学的判决——但能顺手把哲学问题变成编程练习,正是抽象代码的浪漫。
系列其他文章见抽象代码宣言。