qsort 函数指针的类型转换

发布于 2024-12-10 21:27:11 字数 1528 浏览 0 评论 0原文

    #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 字符的指针。手册页似乎为我们提供了指向非常量事物的常量指针,这似乎不是 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 是 const。那么为什么编译器不生成警告呢?我是否遗漏了某些内容,或者是否有理由不应该生成警告?

    #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);
    }

I'm confused with this part:

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

Why did they do this? Why did they NOT do this: (const char**) or (const char * const*)? Don't we get a pointer to a const char if we dereference once for (const char**)? Dereferencing the second one, don't we get a const pointer that points at a const char. Both of these seem to what strcmp() asks for: two pointers who point at const chars. What the man page seems to give us const pointers pointing at non-const things, which doesn't seem to be what strcmp()'s declaration is asking for. Even if legal, it doesn't seem like a good idea to give a function something that doesn't fit its parameters. Am I missing something?

Lastly, why does the following not generate at least an error warning:

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

Dereferencing ptr1 once gives us a pointer to a const char, but is not itself const. However, ptr3 is const. So why doesn't the compiler generate a warning? Am I missing something, or is there a reason it shouldn't be generating a warning?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

謸气贵蔟 2024-12-17 21:27:11

首先是最后一个问题:

这很好:

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 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 的指针转换为指向非 const string 的指针,某些编译器可能会发出警告,提示您正在丢弃限定符。 C++ 编译器甚至不应该让这样的代码通过,因为它们需要 const_castconst void * 转换为 string * (又名const char *const *)。

但是:反向转换没问题。将指向非常量对象的指针传递给 strcmp 是可以的。 strcmp 采用指向 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 关键字已过时。现在它实际上没有任何意义——它是默认的存储类说明符。不要使用自动

Last question first:

This is fine:

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

Here, I can simplify it with a typedef:

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

The const in the local variable (the one underlined with ~~~~~) is not really necessary: it specifies that the local variable ptr3 is const, not that the data it points to is const.

Next question: Why (or why not) not *(const char * const *) ptr1 ?

Well, the type of *(const char * const *) ptr1 is const char * const, or const string if you are using the typedef. But it's only used as an rvalue. There is no difference between a const and non-const rvalue. For example, look at the following code:

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

Obviously, x and y get the same value. So there is no real semantic benefit to adding const here, it is just extra typing.

But: some compilers will give a warning...

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

Since you are casting a pointer to const void to a pointer to non-const string, some compilers may give a warning that you are discarding qualifiers. C++ compilers shouldn't even let such code pass, since they'll demand a const_cast to convert from const void * to string * (a.k.a const char *const *).

However: the reverse conversion is fine. It is fine to pass pointers to non-const objects to strcmp. The fact that strcmp takes pointers to const data indicates that strcmp itself does not modify the data.

There are many ways to write the function that are fine. Here are some examples.

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);

Since you are casting variables anyway, the compiler will let a lot of different potential errors slide, but some compilers may give warnings. Use whichever version suits your coding style.

Final tip: The auto keyword is obsolete. It doesn't really mean anything these days -- it's the default storage class specifier. Don't use auto.

合久必婚 2024-12-17 21:27:11

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

问题的第二部分,任何指针都可以分配给 const 指针。反之则会产生错误。您的显式强制转换消除了编译器对指针原始类型的任何了解。

First part of your question, I think there's some flexibility on where you place the const, so const char ** and char * const * are the same.

Second part of your question, any pointer can be assigned to a const pointer. It's going the other way that generates an error. Your explicit cast removed any knowledge the compiler had about the pointer's original type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文