如何使用qsort函数对char**类型的字符串数组进行排序?
qsort对char**数组无效,却能正常排序二维字符数组? 你遇到的问题本质是两种字符串数组的内存布局完全不同,而你的cmp函数只适配了其中一种情况——这就是为什么换数组声明后qsort能正常工作,第一种却没效果。我来拆解清楚:
1. 先搞懂两种数组的内存差异
情况一:二级指针char**(指针数组)
假设你是这么声明的:
char** strs = malloc(3 * sizeof(char*)); strs[0] = "banana"; strs[1] = "apple"; strs[2] = "cherry";
这里的strs是一个指针,指向一块连续内存,里面存的是三个独立的char*指针——每个指针各自指向一个字符串(可能是字面量,也可能是堆上的字符串)。简单说,这是一个"指针的数组",字符串本身并不在这个数组里,而是在别的内存位置。
情况二:二维字符数组(数组指针)
比如你这么写:
char strs[3][10] = {"banana", "apple", "cherry"};
这是一个完全连续的二维数组:每个字符串直接存在数组的元素里,strs[0]就是第一个字符串的首地址,strs[1]是第二个,以此类推。没有额外的指针层,字符串本身就嵌在数组的连续内存块中。
2. qsort的逻辑与cmp函数的适配问题
qsort的工作方式是:遍历base指向的数组,每次把两个元素的地址传给cmp函数,由cmp判断它们的顺序。这里的核心坑点是——两种数组的元素类型完全不同,所以cmp接收到的参数含义也不一样:
适配char**的正确cmp函数
如果数组是char**,每个元素是char*类型,那么cmp接收到的a和b是指向char*的指针(也就是char**类型)。这时候必须先解引用,才能拿到真正的字符串地址:
int cmp(const void* a, const void* b) { // 把a、b转换成指向char*的指针,解引用后得到字符串首地址 return strcmp(*(const char**)a, *(const char**)b); }
如果你没做这个解引用,直接写strcmp(a, b),那你其实是在比较两个char**指针的地址值(也就是数组里元素的内存地址),而不是字符串内容。如果你的指针数组本来就是按地址递增顺序初始化的,qsort会认为它已经有序,自然不会有任何变化。
适配二维数组的正确cmp函数
如果是二维字符数组,每个元素是char[10]类型,cmp接收到的a和b是指向char[10]的指针(也就是char(*)[10]类型)。这时候直接把它转换成const char*就能用strcmp:
int cmp(const void* a, const void* b) { return strcmp((const char*)a, (const char*)b); }
因为a本身就指向字符串的首字符,不需要额外解引用。
3. 你的代码为什么会出现两种结果?
你说cmp函数和qsort调用完全不变,那大概率你的cmp是适配二维数组的版本(直接用strcmp(a,b))。这时候:
- 在二维数组场景下,
a就是字符串首地址,strcmp正常比较内容,排序自然生效; - 在
char**场景下,a是指向char*的指针,strcmp把它当作char*来比较,实际比的是指针地址,而不是字符串内容——所以排序后的顺序和原顺序几乎没变化。
4. 如何让char**数组正常排序?
只需要修改cmp函数,加上解引用步骤,把a和b转换成const char**后再解引用,拿到真正的字符串地址再比较就行。
内容的提问来源于stack exchange,提问作者Miradil Zeynalli

