编程珠玑中的qsort函数出现错误?

4

是我自己的问题还是这段代码在Programming Pearls中有误(快速排序需要2个const voids,对吗?)如果是这样,那么我的解决方案是正确的吗?抱歉,我正在学习中...

int wordncmp(char *p, char* q)
{   int n = k;
    for ( ; *p == *q; p++, q++)
        if (*p == 0 && --n == 0)
            return 0;
    return *p - *q;
}

int sortcmp(char **p, char **q)
{   return wordncmp(*p, *q);
}
...

qsort(word, nword, sizeof(word[0]), sortcmp);

这是一个解决方案吗?

int sortcmp(const void *p, const void *q)
{   return wordncmp(* (char * const *) p, * (char * const *) q);
}
2个回答

7

第一个代码示例可能适用于几乎所有编译器和CPU;然而,如果您严格遵循C标准,它就是技术上的未定义行为。

正如您所说,qsort()的最后一个参数是指向以const void * 类型的两个参数作为输入的函数的指针。 sortcmp采用不同的参数。您的编译器应该警告您有不兼容的类型签名或其他内容。无论哪种情况,都会将一个函数从一种类型转换为另一种类型。

C标准规定,您可以将函数指针强制转换为具有不同类型的其他函数指针,但是您不能取消引用和调用转换后的函数指针。但是,如果将函数指针重新转换回其原始类型,则调用该函数具有定义的行为-它调用原始函数。

由于您正在从int(*)(char **,char **)转换为int(*)(const void *,const void *),然后最终qsort()在不将其转换回int(*)(char **,char **)的情况下调用比较器函数,这是未定义的行为。

但是,由于在几乎所有架构上,char **const void * 以相同的方式表示,因此函数调用几乎总是有效的。

如果要获得定义的行为,您必须确保比较器函数具有正确的类型签名,然后可以将参数转换为正确的类型。您的解决方案在这方面完全正确,不违反C标准。恭喜您对const的正确性有所了解-很多人不理解char * const *的确切含义。

您还应该使wordncmp()采用const char*参数,因为您不修改参数。

附带说明:您也无法从函数指针强制转换为数据指针(例如void * )或反之亦然。标准允许函数指针和数据指针具有不同的大小。即使在您的计算机上工作了,也不能保证始终能够工作。


标准是否确实保证 char *void * 具有相同的表示形式?我同意使用错误的函数指针类型进行调用仍然是未定义行为,但我不认为参数的表示形式是问题所在。相反,问题在于病态实现可能会对传递 void *char * 使用不同的调用约定,例如为每种类型设置单独的参数寄存器集。 - R.. GitHub STOP HELPING ICE
@R.: 是的,标准确保char*void*具有相同的表示和对齐要求(C99 §6.2.5/27),但char**void*不一定兼容。 - Adam Rosenfield

2
你是正确的,sortcmp的签名与qsort期望的不符合。你的更正是正确的。wordcmp也应该进行const修正,因为在转换过程中你实际上正在失去一些const

int wordncmp(const char *p, const char* q)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接