C如何使用带比较器功能的quicksort

时间:2017-09-18 00:43:54

标签: c pointers quicksort qsort

我试图理解比较器函数stringAsInt(const void *pLeft, const void *pRight)

的前几行中发生了什么
  1. 所以参数是某些东西的常量指针。然后在接下来的两行中我们将void转换为(const char**)为什么它被强制转换为指针的指针?另外,这两行究竟发生了什么?
  2. qsort()函数中调用main()时,为什么没有参数传递给stringAsInt()stringAsInt()如何知道pLeftpRight是什么?
  3. 为什么a被设置为指针的指针?标准阵列不足够吗?
  4. -

    int stringAsInt(const void *pLeft, const void *pRight) {
        const char *left = *(const char**)pLeft;
        const char *right = *(const char**)pRight;
        int leftLen = (int)strlen(left);
        int rightLen = (int)strlen(right);
        if (leftLen != rightLen) {
            return leftLen - rightLen;
        } else {
            return strcmp(left, right);
        }
    }
    
    int main() {
        int n;
        scanf("%d", &n);
        char buffer[1000000 + 1];
        char **a = malloc(sizeof(char*) * (size_t)n);
        for (int i = 0; i < n; i++) {
            scanf("%1000000s", buffer);
            a[i] = malloc(sizeof(char) * (strlen(buffer) + 1));
            strcpy(a[i], buffer);
        }
        qsort(a, (size_t)n, sizeof(a[0]), stringAsInt);
        for (int i = 0; i < n; i++) {
            printf("%s\n", a[i]);
            free(a[i]);
        }
        free(a);
        return 0;
    } 
    

3 个答案:

答案 0 :(得分:2)

评论中的代码细分。

//
//  main.c
//

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

//Takes a pointer to any type.. (void *)
int stringAsInt(const void *pLeft, const void *pRight) {
    //qsort calls this function with a pointer to each element. Since each element is a char* then pLeft is actually a char**.
    //For example, if sorting an array of ints (each element is of type: int), pLeft would be int*.
    //If sorting an array of strings (each element is of type: char*), pLeft would be char**.
    //So on and so forth.


    //Thus we cast the pLeft to its correct type (const char**) for pointer to string.. Then we dereference it to get the string itself (const char*).
    const char *left = *(const char**)pLeft;
    const char *right = *(const char**)pRight;

    int leftLen = (int)strlen(left);
    int rightLen = (int)strlen(right);

    //Compare the lengths of the strings.. If left is < right, we return negative. If they are the same, 0.. else positive..
    if (leftLen != rightLen) {
        return leftLen - rightLen;
    } else {
        return strcmp(left, right);  //Lengths are equal.. compare their contents..
    }
}

int main() {
    int n;

    //First the code takes an array count.. This is the amount of arrays to sort..
    printf("Enter number of strings: ");
    scanf("%d", &n);

    //It allocates a large buffer on the stack to hold entire sentences to sort..
    char buffer[1000000 + 1];

    //Allocates an array of strings..
    char **a = malloc(sizeof(char*) * (size_t)n);

    for (int i = 0; i < n; i++) {
        printf("Enter a string: ");
        scanf("%1000000s", buffer);

        //Store each string in the array..
        a[i] = malloc(sizeof(char) * (strlen(buffer) + 1));
        strcpy(a[i], buffer);
    }

    //Sort the array using stringAsInt comparator..
    //a is the array to sort.
    //n is the amount of elements in the array.
    //sizeof(a[0]) is the size of each element in the array. sizeof(char*).
    //stringAsInt is the comparator function (pointer to function)..

    qsort(a, (size_t)n, sizeof(a[0]), stringAsInt);

    //Print the sorted array and cleanup each element.
    for (int i = 0; i < n; i++) {
        printf("%s\n", a[i]);
        free(a[i]);
    }

    //Cleanup the array itself.
    free(a);
    return 0;
}

答案 1 :(得分:0)

  

因此参数是某些东西的常量指针。然后在接下来的两行中我们将void转换为(const char **)为什么它被强制转换为指针的指针?另外,这两行究竟发生了什么?

它是C中的类型擦除.qsort不知道数组元素有什么类型,它只知道它们的大小并将各个元素作为类型擦除指针传递给比较器,期望比较器将指针转换为所需类型。这正是这两行中发生的事情。字符串数组简单地组织为char的指针数组(字符串的传统C语言)。数组的每个元素都是一个指向字符的指针(实际上,是一个连续字符序列中的第一个)。 qsort将类型擦除指针传递给那些,比较器将它们向下转换回具体类型。

  

在main()函数中调用qsort()时,为什么没有参数传递给stringAsInt()? stringAsInt()如何知道pLeft和pRight是什么?

它是一个指向函数的指针。 qsort的最后一个参数是一个函数指针。然后它调用此函数来比较各个元素对,每次都为pLeft和pRight提供值。

  

为什么要设置为指针的指针?标准阵列不足够吗?

可能,如果您确定您的文件不大。如果它由毫秒的字符串组成,由于该站点的URI的第二级域,该程序可能会崩溃,因此他们决定将整个数组放在堆上。

答案 2 :(得分:0)

  1. stringAsInt中的转换舞蹈是因为qsort()使用const void *参数调用比较器函数。但是,比较器需要引用它们指向的实际结构才能进行比较,因此它会转换为正确类型的const指针。
  2. 在这种情况下,正确的类型是指向char的指针,因为字符串是char *,并且该字符串的地址是char **。 qsort将对字符串数组进行排序。

    C这种方式令人困惑。请考虑以下带有以下字符的内存地址:

    0x0100 'f'
    0x0101 'o'
    0x0102 'o'
    0x0103 '\0'
    
    0x3127 'b'
    0x3128 'a'
    0x3129 'r'
    0x312a '\0'
    

    我们有一个指向这些字符串的指针数组(上述程序中的变量a):

    0x2522: 0x0100
    0x2524: 0x3127
    

    所以a == 0x2522,a [0] = 0x0100(“foo”),a [1] = 0x3127(“bar”)

    qsort()将切换[0]和[1]的内容(因为“bar”在“foo”之前按字典顺序排列);被叫之后唯一的变化是:

    0x2522: 0x3127
    0x2524: 0x0100
    

    字符串本身不会被更改。

    1. qsort例程知道它的第一个元素在哪里,它必须排序多少元素,每个元素有多大 - 它需要知道每个元素的地址(如果数组从0x1000开始,有10个元素,每个元素长度为16个字节,它知道元素0为0x1000,元素1为0x1010等。 qsort例程将根据需要多次调用stringAsInt(),并使用已计算的元素,并交换必须交换的元素以对数组进行排序。

    2. 因为字符串。如果它是整理ints(例如)它将只是一个标准数组。但字符串是char *。