用于qsort函数指针的Typecast

时间:2011-10-21 09:14:18

标签: c casting function-pointers strcmp qsort

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

    static int cmpstringp(const void *p1, const void *p2)
    {
       /* 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(* (char * const *) p1, * (char * const *) p2);
    }

    int main(int argc, char *argv[])
    {
        int j;

        assert(argc > 1);

        qsort(&argv[1], argc - 1, sizeof(argv[1]), cmpstringp);

        for (j = 1; j < argc; j++)
            puts(argv[j]);
        exit(EXIT_SUCCESS);
    }

我对这部分感到困惑:

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

他们为什么这样做?为什么他们不这样做:(const char**)(const char * const*)?如果我们为(const char**)取消引用一次,我们不能获得指向const char的指针吗?取消引用第二个,我们不会得到一个指向const char的const指针。这两个似乎都是strcmp()所要求的:两个指向const chars的指针。该手册页似乎为我们提供了指向非const事物的const指针,这似乎不是strcmp()的声明所要求的。即使合法,给一个不符合其参数的函数似乎也不是一个好主意。我错过了什么吗?

最后,为什么以下内容至少不会产生错误警告:

    auto const char * const ptr3 = *(const char **) ptr1; //where ptr1 is    
    of the form int foo(const void * ptr).

解除引用ptr1一次给我们一个指向const char的指针,但它本身不是const。但是,ptr3是常量。那么编译器为什么不生成警告呢?我错过了什么,或者是否有理由不应该产生警告?

2 个答案:

答案 0 :(得分:2)

最后一个问题:

这很好:

const void *ptr = ...;
const char * const ptr3 = *(const char **) ptr1;
^^^^^^^^^^^^ ~~~~~          ^^^^^^^^^^^^
     |                            |
     +----------------------------+

在这里,我可以使用typedef简化它:

typedef const char * string;
const void *ptr = ...;
const string ptr3 = *(string *) ptr;
~~~~~ ^^^^^^          ^^^^^^
        |                |
        +----------------+

局部变量中的const(带有~~~~~下划线的那个)并不是必需的:它指定局部变量 ptr3为{{ 1}},而不是它指向的数据是const。

下一个问题:为什么(或为什么不)const

如果您使用的是typedef,则*(const char * const *) ptr1的类型为*(const char * const *) ptr1const char * const。但它只用作右值。 const和非const rvalue之间没有区别。例如,请查看以下代码:

const string

显然,void *ptr = ...; int x = *(const int *) ptr; int y = *(int *) ptr; x获得相同的价值。因此,在这里添加y没有真正的语义上的好处,它只是额外的输入。

但是:一些编译器会发出警告......

const

由于您将指向const void *ptr = ...; string ptr2 = *(string *) ptr; 的指针转换为指向非const const void的指针,因此某些编译器可能会发出警告,指出您正在放弃限定符。 C ++编译器甚至不应该让这些代码通过,因为他们要求stringconst_cast转换为const void *(a.k.a string *)。

但是:反向转换很好。可以将指向非const对象的指针传递给const char *const *strcmp指向const数据的事实表明strcmp本身不会修改数据。

有很多方法可以编写很好的函数。以下是一些示例。

strcmp

由于您无论如何都要编译变量,编译器会让许多不同的潜在错误滑落,但某些编译器可能会发出警告。使用适合您编码风格的版本。

最后提示: return strcmp(*(char **) p1, *(char **) p2); return strcmp(*(const char **) p1, *(const char **) p2); return strcmp(*(char * const *) p1, *(char * const *) p2); return strcmp(*(const char * const *) p1, *(const char * const *) p2); // The following are not technically portable, but are portable in practice return strcmp(*(void **) p1, *(void **) p2); return strcmp(*(const void **) p1, *(const void **) p2); return strcmp(*(void * const *) p1, *(void * const *) p2); return strcmp(*(const void * const *) p1, *(const void * const *) p2); 关键字已过时。这些日子并没有任何意义 - 它是默认的存储类说明符。请勿使用auto

答案 1 :(得分:0)

您的问题的第一部分,我认为const的位置有一定的灵活性,因此const char **char * const *是相同的。

问题的第二部分,任何指针都可以分配给const指针。它会以另一种方式产生错误。您的显式转换删除了编译器对指针原始类型的任何知识。