#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
是常量。那么编译器为什么不生成警告呢?我错过了什么,或者是否有理由不应该产生警告?
答案 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 *) ptr1
或const 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 ++编译器甚至不应该让这些代码通过,因为他们要求string
从const_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
指针。它会以另一种方式产生错误。您的显式转换删除了编译器对指针原始类型的任何知识。