我刚开始学C。任何帮助都是感激不尽的!
我有一个指向结构体的指针数组,我想使用内置的qsort function根据指针指向的结构体中的值对数组进行排序。我正在尝试使用official docs中演示的比较函数。
以下版本失败:
int compare_nodes(const void* a, const void* b){
const struct ListNode * ptr1 = ((const struct ListNode *) a);
const struct ListNode * ptr2 = ((const struct ListNode *) b);
// const struct ListNode * ptr1 = *((const struct ListNode **) a);
// const struct ListNode * ptr2 = *((const struct ListNode **) b);
int arg1 = ptr1 -> val;
int arg2 = ptr2 -> val;
if(arg1 < arg2) return -1;
if(arg1 > arg2) return 1;
return 0;
}
此版本成功:
int compare_nodes(const void* a, const void* b){
// const struct ListNode * ptr1 = ((const struct ListNode *) a);
// const struct ListNode * ptr2 = ((const struct ListNode *) b);
const struct ListNode * ptr1 = *((const struct ListNode **) a);
const struct ListNode * ptr2 = *((const struct ListNode **) b);
int arg1 = ptr1 -> val;
int arg2 = ptr2 -> val;
if(arg1 < arg2) return -1;
if(arg1 > arg2) return 1;
return 0;
}
我不明白这两个版本之间的区别:
1.如果强制转换只告诉编译器如何解释指针指向的地址,那么版本1中的问题是什么?告诉编译器将指向void的指针解释为指向struct ListNode的指针还不够吗?为什么我需要通过强制转换添加一层间接寻址,然后通过解引用删除一层间接寻址?
- C的按值传递在这里起作用吗?我自己想不出任何理由。
我找到了以下关于这个问题的资源。虽然它们似乎解释了这个问题(特别是资源6),但我并不理解它们: - What are the rules for casting pointers in C?
- Typecasting of pointers in C
- Pointer type casting and dereferencing
- What are the rules for casting pointers in C?
- What does a C cast really do?
- https://cboard.cprogramming.com/c-programming/102056-casting-pointer-pointer.html
下面是完整的代码:
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
struct ListNode {
int val;
struct ListNode *next;
};
int calc_list_length(struct ListNode * head){
int target = 0;
struct ListNode * tmp = head;
while (tmp)
{
target++;
tmp = tmp -> next;
}
return target;
}
int compare_nodes(const void* a, const void* b){
// const struct ListNode * ptr1 = ((const struct ListNode *) a);
// const struct ListNode * ptr2 = ((const struct ListNode *) b);
const struct ListNode * ptr1 = *((const struct ListNode **) a);
const struct ListNode * ptr2 = *((const struct ListNode **) b);
int arg1 = ptr1 -> val;
int arg2 = ptr2 -> val;
if(arg1 < arg2) return -1;
if(arg1 > arg2) return 1;
return 0;
}
struct ListNode* sortList(struct ListNode* head){
if(!head) return NULL;
int list_length = calc_list_length(head);
struct ListNode * tmp = head;
struct ListNode * arr[list_length];
for (int i = 0; i < list_length; i++)
{
arr[i] = tmp;
tmp = tmp -> next;
}
for (int i = 0; i < list_length; i++) {
printf("%d ", arr[i] -> val);
}
printf("\n");
qsort(arr, list_length, sizeof(struct ListNode *), compare_nodes);
for (int i = 0; i < list_length; i++) {
printf("%d ", arr[i] -> val);
}
printf("\n");
}
int main(){
// [2,1,4,3]
struct ListNode node4 = {.val = 3, . next = NULL};
struct ListNode * ptr4 = &node4;
struct ListNode node3 = {.val = 4, .next = ptr4};
struct ListNode * ptr3 = &node3;
struct ListNode node2 = {.val = 1, .next = ptr3};
struct ListNode * ptr2 = &node2;
struct ListNode node1 = {.val = 2, .next = ptr2};
struct ListNode * ptr1 = &node1;
sortList(ptr1);
getchar();
return 0;
}
先谢谢你了。希望你能给我指个方向。
3条答案
按热度按时间wswtfjt71#
qsort
使用指向运算符&
将指针传递给数组元素。例如,它可以将
&arr[0]
和&arr[1]
作为参数传递给比较函数。由于
arr
是一个指针数组,其中每个元素都是一个指针,因此根据定义,指向一个元素的指针必须是指向另一个指针的指针。因此,传递给
compare_nodes
结构的参数是指向ListNode
结构的指针。w80xi6nr2#
函数qsort声明如下
这个函数处理
void *
类型的指针,它将const void *
类型的两个指针传递给比较函数,这两个指针指向底层数组的元素。您声明了一个指针数组
其
ListNode *
类型的元素通过指向它们的指针的引用传递给比较函数。实际上,函数将
ListNode **
类型的指针传递给比较函数,这些指针作为const void *
类型的指针传递。其中表达式
&arr[i]
具有类型ListNode **
。当然,函数
qsort
实际上并不知道数组元素的实际类型。因此,在比较函数中,您需要执行“反向”转换,如
其中
ptr1
是原始数组的元素,通过指向它的指针的引用将该元素传递给比较函数。o2g1uqev3#
qsort()
函数将指向数组元素的指针传递给比较器函数,而不是直接按值传递数组元素,这是由于qsort()
的设计方式所致,核心C语言本身并不需要。我可以很容易地编写一个排序函数,对指针数组进行排序,使用以下签名:
并且它接受一个直接传递数组元素(指针)的比较函数,这样你的比较函数就不需要解引用参数来获得要比较的实际值。
然而,我上面的排序函数不能排序,比如说,一个
float
数组,因为它不能把两个float
参数传递给一个应该接受两个指针的比较函数。即使你创建了一个接受两个float
的比较函数,你也不能把它传递给上面的函数,因为函数指针类型是不兼容的。即使您以某种方式强制转换了类型,它仍然无法工作,因为函数内部的C代码认为它调用的是一个接受两个指针的函数,却无法调用实际接受两个float
的函数,因为函数签名不兼容。因此,为了能够在这种设计下对不同类型的数组进行排序,我必须有另一个排序函数来对
float
的数组进行排序:另一个排序函数对
int
的数组进行排序,等等。由于结构体的类型不限,大小也不一样,所以你不可能创建一个函数来处理所有的结构体。即使只支持基本类型,它将需要几种不同的功能。C标准库的设计人员认为,最好是使用一个适用于所有类型的排序函数,通过将指向数组元素的指针传递给比较器函数。当您了解C语言的局限性时,这种设计非常有意义。(在C++中,可以使用泛型来编写一个通用的排序函数,该函数适用于任何类型,并且具有一个按值传递数组元素的比较器。但C没有泛型。)但是,这是
qsort()
函数API的设计决定。和排序函数(至少在它们只接受特定类型的情况下)并不一定要以这种方式工作,所以只有通过阅读qsort()
的规范才能知道qsort()
应该以这种方式使用。