c中的qsort类型转换指针

qsort type casting pointers in c

这是一个简单的C语言练习qsort函数的代码

int compareF(const void *a, const void *b) {
    // return (*(int**)a)[0] - (*(int**)b)[0];  
    const int *pr1 = *(const int **)a;
    const int *pr2 = *(const int **)b;
    return pr1[0] - pr2[0];
}

int main() {
    int *array[] = {(int[]){2,2,3}, (int[]){1,2,1},(int[]){1,3,3},(int[]){0,2,3},(int[]){1,2,0}};
    qsort(array, 5, sizeof(int[3]), compareF);
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", array[i][j]);
        }
        printf("\n");
    }
  return (0);
}

我不明白第 3 行和第 4 行为何使用指针以这种方式进行类型转换。这是我的理解。首先,无论我们是否设置新变量来保存参数,我们如何使用语法都没有关系,就像这两个 return 状态一样。 其次,它需要区分类型,因为参数是 void 类型,所以函数不知道类型。第一个 * 用于取消引用参数 a,以便它可以访问地址 a。我们使用 int** 因为..?

main函数中有二维整数数组。在我的理解中,CompareF 函数每次只将 array[0] 和 array[1] 作为参数。所以我不明白为什么用两个 **.

来打字

同样的代码,只修改了一行代码

int array[3][2]= {{1,4},{3,6},{2,8}};

当这样定义数组时,类型转换是否以同样的方式发生?

const int *pr1 = *(const int[])a;
const int *pr2 = *(const int[])b;

const int *pr1 = (const int*)a;
const int *pr2 = (const int*)b;

第一种情况无效,第二种情况有效。但是它不需要 * 来取消引用吗?喜欢

const int *pr1 = *(const int*)a;
const int *pr2 = *(const int*)b;

传递给 qsort 的比较函数在要比较任何两个数组元素的任何时候调用,它获取每个数组元素的地址。

您有一个正在排序的 int * 数组,因此每个元素的地址都具有类型 int **。这与 compareF 的参数转换为的类型相匹配。取消引用这些指针然后给我们一个 int * 类型的值,这就是数组中实际包含的值。

此外,qsort 的第三个参数不正确。数组元素的类型不是 int[2],而是类型 int *。如果 int 是 4 个字节,而指针是 8 个字节,那么它们恰好是相同的,但你不能依赖它。

对于初学者来说,这个 qsort 调用:

qsort(array, 5, sizeof(int[2]), compareF);

不正确。第三个参数是数组元素的大小。由于数组元素的类型是 int * 那么第三个参数必须是 sizeof( int * ) 或者是相同的 sizeof( *array ):

qsort(array, 5, sizeof( *array ), compareF);

函数qsort将指向数组元素的指针作为const void *.

类型的指针传递给比较函数compareF

作为元素,如果数组的类型为 int *,则指向数组元素的指针的类型为 int **

因此在函数 compareF 中,您需要将类型 const void * 的指针转换为类型 const int **。取消引用这样的指针:

const int *pr1 = *(const int **)a;

你会得到原始数组的一个元素。

由于数组的元素指向具有类型 int[3] 的复合文字的第一个元素,因此例如比较函数中的表达式 pr1[0] 给出了复合文字的第一个元素,表达式 pr1[1] 给出复合文字的第二个元素,依此类推。

通常使用此 return 语句:

return pr1[0] - pr2[0];

是不正确的,因为提供的表达式可能会导致带符号整数类型 int 的溢出。

比较复合文字的所有元素,对原始数组的元素(指向复合文字的第一个元素的指针)进行排序更有趣。

这里有一个演示程序:

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

enum { M = 3 };

int compareF( const void *a, const void *b ) 
{
    const int *pr1 = *( const int ** )a;
    const int *pr2 = *( const int ** )b;
    
    size_t i = 0;
    while ( i != M && pr1[i] == pr2[i] ) ++i;
    
    return i == M ? 0 : ( pr2[i] < pr1[i] ) - ( pr1[i] < pr2[i] );
}

int main(void) 
{
    int * array[] = 
    {
        (int[M]){2,2,3}, 
        (int[M]){1,2,1},
        (int[M]){1,3,3},
        (int[M]){0,2,3},
        (int[M]){1,2,0}
    };
    
    const size_t N = sizeof( array ) / sizeof( *array );
    
    qsort( array, N, sizeof( *array ), compareF );
    
    for ( size_t i = 0; i < N; i++ )
    {
        for ( size_t j = 0; j < M; j++ )
        {
            printf( "%d ", array[i][j] );
        }
        
        putchar( '\n' );
    }
}

程序输出为

0 2 3 
1 2 0 
1 2 1 
1 3 3 
2 2 3 

如您所见,如果两个复合文字的第一个元素彼此相等,则比较第二个元素,依此类推。

如果你有一个 two-dimensional 数组声明为

int array[3][2]= {{1,4},{3,6},{2,8}};

那么表示元素类型又是数组类型int[2]。所以函数调用看起来像

qsort(array, sizeof( array ) / sizeof( *array ), sizeof( int[2] ), compareF);

并且指向数组元素的指针的类型为 int ( * )[2]
因此,在比较函数中取消引用指针,您将得到一个 int[2] 类型的数组(原始数组的一个元素),用作表达式的数组被隐式转换为指向其第一个元素的指针。

这里有一个演示程序。

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

enum { M = 2 };

int compareF( const void *a, const void *b ) 
{
    const int *pr1 = *( const int ( * )[M] )a;
    const int *pr2 = *( const int ( * )[M] )b;
    
    size_t i = 0;
    while ( i != M && pr1[i] == pr2[i] ) ++i;
    
    return i == M ? 0 : ( pr2[i] < pr1[i] ) - ( pr1[i] < pr2[i] );
}

int main(void) 
{
    int array[][M] = 
    {
        { 1, 4 }, { 3, 6 }, { 2, 8 }
    };
    
    const size_t N = sizeof( array ) / sizeof( *array );
    
    qsort( array, N, sizeof( *array ), compareF );
    
    for ( size_t i = 0; i < N; i++ )
    {
        for ( size_t j = 0; j < M; j++ )
        {
            printf( "%d ", array[i][j] );
        }
        
        putchar( '\n' );
    }
}

程序输出为:

1 4 
2 8 
3 6 

注意当两个比较元素相等时,比较函数应return0。相对于函数compareF表示循环后:

    size_t i = 0;
    while ( i != M && pr1[i] == pr2[i] ) ++i;

i 将等于 M 因为比较的 one-dimensional 数组的所有元素彼此相等。