【问题标题】:Typecast for qsort function pointerqsort 函数指针的类型转换
【发布时间】:2011-10-21 09:14:18
【问题描述】:
    #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 字符的指针。手册页似乎给了我们指向非常量事物的 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 是常量。那么为什么编译器不生成警告呢?我是否遗漏了什么,或者是否有理由不应该产生警告?

【问题讨论】:

  • 忘记 auto 是 C 中的关键字 - 或者不要使用它。 C++11 定义了它的用途,但在 C99 中它是噪音。

标签: c casting function-pointers strcmp qsort


【解决方案1】:

最后一个问题:

这很好:

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 *) ptr1 的类型是 const char * const,如果您使用 typedef,则为 const string。但它仅用作右值。 const 和非 const 右值之间没有区别。比如看下面的代码:

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

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

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

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

由于您将指向 const void 的指针转换为指向非常量 string 的指针,因此某些编译器可能会发出警告,表明您正在丢弃限定符。 C++ 编译器甚至不应该让这样的代码通过,因为它们会要求 const_castconst void * 转换为 string *(又名 const char *const *)。

但是:反向转换没问题。可以将指向非常量对象的指针传递给strcmpstrcmp 采用指向 const 数据的指针这一事实表明 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

【讨论】:

  • 感谢您的详尽回复。但是,有一件事让我难以理解。您说:“由于您将指向 const void 的指针转换为指向非常量字符串的指针,因此某些编译器可能会警告您正在丢弃限定符。”我感到困惑的是 const void *ptr 是一个指向 const 数据的 void 指针。但是 (string *) 和 (const char *) 一样,都是指向 const 数据的指针。为什么编译器会抱怨这个?听起来好像 void 指针是一个指向 const 数据的 const 指针。但这不可能。那为什么要选演员呢?
  • 不,string *const char * 不同,它与const char ** 相同。这就是我使用 typedef 的原因。如果您将const void *(指向 const 的指针)转换为 string *(指向非 const 的指针),那么一些编译器会抱怨。
【解决方案2】:

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

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

【讨论】:

  • 有一定的灵活性,但const char **(指向 const char 的指针)仅等效于 char const **,而char *const *(指向 const 指向 char 的指针)则不同。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-09
  • 2019-06-16
  • 2012-06-07
  • 2017-10-06
  • 2022-08-19
  • 2020-06-15
  • 1970-01-01
相关资源
最近更新 更多