【问题标题】:const qualifier in qsort compare functionqsort 比较函数中的 const 限定符
【发布时间】:2021-08-04 04:29:23
【问题描述】:

我用 qsort 以两种方式对字符串数组(argv 本身)进行排序。我从 linux man 3 qsort 示例开始。那里cmpstringp只有一行:

 /* The actual arguments to this function are "pointers to
    pointers to char", but strcmp(3) arguments are "pointers
    to char", hence the following cast plus dereference */

 return strcmp(*(const char **) p1, *(const char **) p2);

我试图重新表述它,它编译时没有警告只有

char *const* sp1 = vep1

const 位于中间 - 它是 argv 条目 (?)。至少这是 qsort 声明所要求的。上面从手册页转换的内容似乎不同且复杂。

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

/* vep: void pointer to elements being qsorted 
   sp: string pointer (char **)            */
int 
cmpstringp(const void *vep1, const void *vep2) {

    char *const *sp1 = vep1,
         *const *sp2 = vep2;

    return strcmp(*sp1, *sp2);
}
/* Make char* from void*, and even char    */
int 
cmpchar(const void *p1, const void *p2) {

    const char *cp1 = p1,
               *cp2 = p2;

    char c1 = *cp1,
         c2 = *cp2;

    if (c1 == c2)
        return 0;
    else
        return c1 > c2 ? 1 : -1;
}

/* Sort cmd line args in two ways with qsort */
int main(int argc, char **argv) {

    /* sort chars of each string */
    for (int i = 1; i < argc; i++)
    qsort(argv[i],                  // base / first  
          strlen(argv[i]), 1,       // n_elems, elem_size
          cmpchar);

    /* sort argv strings 1 to argc-1 */
    qsort(argv + 1,
          argc - 1, sizeof *argv,
          cmpstringp);

    for (int j = 1; j < argc; j++)
        puts(argv[j]);

    return 0;
}

第二个 cmp 函数 cmpchar 有一个额外的分配级别。没有就会有一些像if (*cp1 == *cp2)这样无害的明星。

How do I sort the elements of argv in C? 有一个类似手册页的解决方案,直接在strcmp() 中进行转换。

但是我的免演员方法不是更正确吗?在我将 const 放在它所属的位置之前,我收到了警告。

【问题讨论】:

  • cp1sp1 不是空指针。
  • 免费explicit casts
  • @user3386109,你是对的。 OP 对其参数阻抗问题的解决方案将隐式转换部署到中介,而不使用任何强制转换运算符。代码生成和质量方面的影响可以说是 NIL,但我太累了,无法进一步争论更精细的问题。

标签: c argv qsort


【解决方案1】:

char *const *sp1 = vep1中间 中的const 表示*sp1 指定的任何内容都是const。这是必需的,因为const void * 表示指针直接指向的内存应该是const

你是对的,const void * 在没有强制转换的情况下不能分配给const char **,因为它不会是 const 正确的。我个人尽量避免强制转换,因此我更喜欢你的代码而不是使用强制转换的代码。

不过,使用const char *const *sp1 会更正确一些,因为cmpstringp 没有理由实际上也适用于const char **

【讨论】:

  • 郑重声明,我完全同意,避免显式强制转换是最佳做法。
  • Re “但是使用 const char *const *sp1 会更正确”:这使得行为未定义。如果qsort 传递了char **,然后比较例程定义了const char * const *sp1 = vep1 并使用*sp1,它将访问sp1 指向的char *,其左值类型为const char *。这不是 6.5 7 中的别名规则所允许的任何情况:它们不兼容,后者不是与前者兼容的类型的合格版本,它们不是有符号/无符号对,后者不是聚合或联合,它不是字符类型。
  • char *const char * 根据 C 2018 6.7.6.1 2 不兼容:“对于要兼容的两种指针类型,两者应具有相同的限定,并且都应是指向兼容类型的指针, ”和 6.7.3 11:“要兼容的两个合格类型,两者都应具有兼容类型的相同合格版本……”)
  • @EricPostpischil 是的,尽管我并不完全理解规范中的所有这些。说明:在 main 中使用 const char **argv,由于 qsort 的第一个 arg(基数)需要 void *,因此会出现丢弃常量警告。排序工作:字符没有被修改,只是移动了!正式地。使用整个结构会更有意义。 Qsort 实际上希望基数为void *,比较参数为const void *。由于调用层次结构而有意义。只是显得既迂腐又自相矛盾。
  • @BraCas: main 不应使用const char *argv[] 声明。 int main(int argc, char *argv[]) 是正确的。
【解决方案2】:

重新排序和对齐,从右到左变得更清楚什么是转换的,什么是保留的

   int cmpstringp(void const *vep1, const void *vep2) {
    
                char * const *sp1    = vep1, ...

const 被复制,“to void”被“to 指针(to char)替换。它甚至不必是指针(例如结构)。如果,那么我怀疑这个 const- ness 可以传播到所有引用上,只是因为“比较”函数根本不应该修改。它可以修改所有对象,但不能修改正在排序的对象。

这一切都很有趣,但逻辑上很复杂。就像有一排盒子里面有字母。首先,您根据自己对每个框内的字母进行排序(不触摸框),然后根据字母对框进行排序(但不触摸它们,字母)。

感谢cmets和回答。


6.5.16.1 简单赋值可能会处理这种情况。

指针分配的 3 个项目符号似乎需要:

  • 兼容的类型,或者一个是无效的
  • 左侧包含右侧的所有限定符。
  • 右边的空指针没问题

【讨论】:

    猜你喜欢
    • 2014-10-10
    • 2018-03-26
    • 2019-07-01
    • 1970-01-01
    • 2018-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多