4

当我学习使用 qsort 对字符串数组进行排序时,有一个问题让我很困惑。例如,对以下内容进行排序s

 char *s[] = {
                   "Amit",
                   "Garima",
                   "Gaurav",
                   "Vaibhav"
                };

要使用 qsort,你必须提供一个比较函数,cstring_cmp我猜在 qsort 函数中,要传递给函数的参数类型cstring_cmpchar**. 如何将 a 转换char**为 a void*?为什么我们可以将 a 转换char**为 a void*

    int cstring_cmp(const void *a, const void *b)
    {
        const char **ia = (const char **)a;
        const char **ib = (const char **)b;
        return -strcasecmp(*ia, *ib);
        /* return the negative of the normal comparison */
    }
4

3 回答 3

2

你的问题似乎有点模糊,但无论如何我都会试一试。要回答如何,您可以通过简单的强制转换将任何指针类型转换为 C 中的任何其他指针类型。要回答为什么,这就是 C 的定义方式。

qsort()函数需要一个具有给定原型(带有const void *)参数的函数。这是因为qsort()不知道您正在排序的实际数据类型,并且必须使用一致的函数原型进行比较回调。在您的情况下,您的比较回调负责将const void *参数转换为指向数组中实际类型的指针const char **

于 2012-11-05T03:31:05.943 回答
2

您提供的示例被设置为要求qsort()对 char 指针 (char *) 数组进行排序。您提供的这个比较器通过 address给出了算法需要的每“对”项目。两个字符指针。地址qsort()使用基于您给它的根地址,每个“项目”添加大小字节。由于每个“项目”都是一个 char*,因此每个项目的大小实际上就是一个指针的大小。

我已经修改了比较器以演示正在比较的内容以及传入的地址。您将看到它们都是从包含所有 char *s 的数组的基地址开始的增量。

char *mystrings[] =
{
    "This",
    "is",
    "a",
    "test",
    "of",
    "pointers",
    "to",
    "strings"
};

int cstring_cmp(const void *a, const void *b)
{
    const char **ia = (const char **)a;
    const char **ib = (const char **)b;
    printf("%p:%s - %p:%s\n", a, *ia, b, *ib);
    return -strcasecmp(*ia, *ib);
}

int main(int argc, char *argv[])
{
    printf("Base address of our pointer array: %p\n\n", mystrings);
    qsort(mystrings, sizeof(mystrings)/sizeof(mystrings[0]), sizeof(char*), cstring_cmp);
    for (size_t i=0; i<sizeof(mystrings)/sizeof(mystrings[0]);i++)
        printf("%s\n", mystrings[i]);
    return 0;

}

产生以下输出:

Base address of our pointer array: 0x100006240

0x100006240:This - 0x100006260:of
0x100006260:of - 0x100006278:strings
0x100006240:This - 0x100006278:strings
0x100006248:is - 0x100006240:strings
0x100006278:This - 0x100006240:strings
0x100006250:a - 0x100006240:strings
0x100006270:to - 0x100006240:strings
0x100006258:test - 0x100006240:strings
0x100006260:of - 0x100006240:strings
0x100006268:pointers - 0x100006240:strings
0x100006260:of - 0x100006240:strings
0x100006240:test - 0x100006248:This
0x100006248:test - 0x100006250:to
0x100006240:This - 0x100006248:to
0x100006260:of - 0x100006268:pointers
0x100006268:of - 0x100006270:a
0x100006270:a - 0x100006278:is
0x100006268:of - 0x100006270:is
to
This
test
strings
pointers
of
is
a
于 2012-11-05T03:57:48.940 回答
0

一个更不可视化的:

int cstring_cmp(const void *a, const void *b){
    return -strcasecmp((char *)(*((char **)a)), (char *)(*((char **)b)));
}

但是您可以看到 , aand bare char **,它们被取消引用并成为char *并传递给strcasecmp


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int cstring_cmp(const void *a, const void *b){
    return -strcasecmp((char *)(*((char **)a)), (char *)(*((char **)b)));
}

int main(){
    char *s[] = {  "Amit",
                   "Garima",
                   "Vaibhav",
                   "Gaurav"};
    qsort(s, 4, sizeof(char *), cstring_cmp);
    printf("%s\n%s\n%s\n%s\n", s[0], s[1], s[2], s[3]);
    return 0;
}

输出:

Vaibhav
Gaurav
Garima
Amit

char *将任何指针强制转换为或void *因为void *表示指向内存(RAM 或虚拟)字节的指针是合法的。

于 2012-11-05T04:23:04.423 回答