Context
我正在学习C,遇到了一个使用qsort
对字符串数组进行排序的例子。
常见问题:
我正在努力理解以下内容:
1.为什么这两个return语句不同?
1.为什么我们需要将void指针转换为(char**)而不是(char *)。
int CompareWords(const void *a, const void *b) {
char **str1 = (char **)(a);
char **str2 = (char **)(b);
char *c1 = (char *)a;
char *c2 = (char *)b;
return strcmp(c1, c2);
// return strcmp(*str1, *str2);
}
字符串
3条答案
按热度按时间xqkwcwgp1#
qsort
例程不知道您正在排序什么。它接受要排序的东西作为一个数组,您可以为其提供每个元素中的字节数和元素数。由于
qsort
知道每个元素的字节数,因此它可以计算每个元素的地址。但它不知道每个元素是什么类型。因此,当它希望您的例程比较两个元素时,它会使用类型const void *
将两个元素的地址传递给该例程。void
是实际类型的替代品,const
意味着那些地址上的数据不打算被更改,只是比较。你的程序知道元素的类型。数组包含指向字符的指针。具体来说,字符的类型为
char
,指向它们的指针为char *
。因此,qsort
传递给您的地址实际上是char *
的地址,即:char * *
。除了qsort
传递给你一个指向const
数据的指针,所以数据是char * const
,指向它的指针是char * const *
。请注意,char * const *
是指向char
的常量指针的指针,而不是指向常量char
的指针。回顾一下:
char *
。qsort
传递给你数组中一个元素的地址,所以它传递给你一个char * *
。qsort
添加了一个const
,所以它传递给你一个char * const *
。qsort
不知道你的元素类型,所以它将char *
部分更改为void
,并传递给你一个void const *
,它与const void *
相同。char * const *
。您可以通过以下方式将
qsort
传递的地址转换为实际类型:字符串
当您以这种方式正确使用
const
时,您不需要强制转换。编译器将允许在初始化中隐式转换,因为允许从void *
隐式转换为指向对象类型的其他指针,但不允许隐式删除const
。删除const
需要一个铸件。但是使用强制转换意味着const
可能会被意外删除,因此应该避免这种情况在初始化中使用隐式转换而不是强制转换。除了不改变指向
char
的指针之外,这个比较例程也不会改变char
,所以我们可以为它们添加const
,作为一个安全功能,有助于避免错误:型
接下来,我们得到了指向指针的指针,但我们需要使用的是后者。我们可以使用
*str1
和*str2
来获取str1
和str2
指向的指针:型
现在
c1
和c2
指向要比较的实际字符。然后我们可以与
return strcmp(c1, c2);
进行比较,这使得整个例程:型
写出
c1
和c2
的用法主要是为了说明。我们也可以将例程写成:型
为什么这两个return语句不同?
问题中出现的
return strcmp(c1, c2);
是错误的,因为这些c1
和c2
只是从传递的地址转换而来的值。它们是我们需要的指针的地址,而不是我们需要的指针。为什么我们需要将void指针转换为(char**)而不是(char *)。
给
qsort
排序的数组是指向char
的指针数组,qsort
将比较例程指针传递给这些指针。它不传递指针本身。(It无法传递指针本身,因为它不知道数组中的元素是什么类型。它不知道它们是指针,在C中没有办法传递一个你不知道类型的对象的值。传递对象的值需要从内存中获取对象的字节并解释它们,如果不知道它们的类型,就无法解释它们。)
ujv3wf0j2#
为什么这两个return语句不同?为什么我们需要将空指针转换为(char**)而不是(char *)。
您可以在比较函数中使用其中一个return语句。正确的选择取决于排序的内容。
函数qsort的declaratopn如下所示
字符串
第一个函数参数是所传递数组的第一个元素的地址。第二个参数指定传递的数组中的元素数。第三个参数说明符表示传递的数组的元素大小。最后,第四个参数规定了比较函数。
函数
qsort
将指向排序数组的元素的指针作为空指针传递给比较函数,所述空指针用于比较由比较函数内的指针所指向的数组的原始元素。例如,如果您有一个整型数组,例如
型
则用于比较阵列的一对元素(例如第一和第二元素)的函数
qsort
将下面的表达式( const void * )&a[0]
和( const void * )&a[1]
传递给比较函数。因此,您可以通过将指针强制转换为const int *
类型并取消引用所获得的指针来比较比较函数中的元素。让我们考虑一个指向字符串的指针数组。
它看起来像
型
也就是说,数组的每个元素都具有
char *
类型。因此,函数qsort
将指向数组元素(例如&s1[0]
和&s1[1]
)的指针传递给比较函数。这些指针的类型为char **
。所以事实上你有
型
要获取排序数组的元素(即指针),您需要编写
型
也就是说,你首先需要得到
char **
类型的原始指针,然后去引用这些指针,以得到char *
类型的数组的元素的原始类型。现在让我们考虑另一个数组。我们将声明一个二维数组,而不是指向字符串的指针,如
型
同样,函数
qsort
将指向其元素的指针传递给比较函数。但现在数组元素的类型是
char [6]
。例如,在表达式&s1[0]
中,指向这样的元素的指针依次具有类型char( * )[6]
。整个数组的地址值等于其第一个元素的地址值。
您可以使用以下代码片段来检查
型
此代码段的输出可能如下所示
型
因此,逻辑上可以按以下方式来调用cpmpresome函数
型
指针
&s2[0]
和&s2[1]
与指针c1
和c2
具有相同的值。这些值是二维数组中所传递的子数组的第一个字符的地址。这是一个演示程序。
型
程序输出为
型
当然,您可以按以下方式编写函数
CompareWords2
型
但是,当您需要排序另一个二维数组元素的其他型别(而非
char[6]
型别)时,使用函式并不是个好主意,因为当程式码的读者看到排序数组元素的实际型别不同时,这种函式只会让他们感到困惑。hwamh0ep3#
为什么我们需要(char**)在comparator函数中转换字符串?
造型本身是不需要的。
演员表的作用是失去
const
-ness。Like代码可以不进行强制转换。
字符串
为什么这两个return语句不同?
它们服务于不同的目标。正确的使用取决于
qsort()
的使用方式。return strcmp(*str1, *str2)
可能是正确的,因为传递给qsort()
的比较函数期望比较对象的地址。