1
    #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*)?如果我们取消引用一次 for ,我们不会得到一个指向 const char 的指针(const char**)吗?取消引用第二个,我们不会得到一个指向 const char 的 const 指针。这两个似乎都strcmp()要求:两个指向 const 字符的指针。手册页似乎给了我们指向非 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是常量。那么为什么编译器不生成警告呢?我是否遗漏了什么,或者是否有理由不应该产生警告?

4

2 回答 2

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局部变量中的 (带有下划线的那个)~~~~~实际上并不是必需的:它指定局部变量 ptr3const,而不是它指向的数据是 const。

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

好吧,类型*(const char * const *) ptr1const char * const,或者const string如果您使用的是 typedef。但它仅用作右值。const 和非 const 右值之间没有区别。例如,看下面的代码:

void *ptr = ...;
int x = *(const int *) ptr;
int y = *(int *) ptr;

显然,xy得到相同的值。所以在这里添加并没有真正的语义好处const,它只是额外的输入。

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

const void *ptr = ...;
string ptr2 = *(string *) ptr;

由于您将指针转换const void为指向 non-const 的指针string,因此某些编译器可能会警告您正在丢弃限定符。C++ 编译器甚至不应该让这样的代码通过,因为它们会要求 aconst_cast转换const void *string *(aka 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已过时。如今,它实际上没有任何意义——它是默认的存储类说明符。不要使用auto.

于 2011-10-22T04:13:01.237 回答
0

您问题的第一部分,我认为您放置 , 的位置有一些灵活性const,所以const char **char * const *是相同的。

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

于 2011-10-22T03:36:20.397 回答