2011-10-21 13 views
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); 
    } 

私はこの部分で混乱している:型キャストのqsort関数ポインタのための

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

は、なぜ彼らはこれをしましたか?なぜ彼らはこれをしませんでした:(const char**)または(const char * const*)(const char**)のために一度参照を解除すると、const charへのポインタを取得しませんか? 2つ目は、const charを指すconstポインタを取得しないでください。これらの両方は、strcmp()が求めるものです:const charsを指す2つのポインタ。マニュアルページで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はconstです。だから、コンパイラが警告を生成しないのはなぜですか?私は何かを逃しているのですか、それとも警告を発してはならない理由がありますか?

+0

「auto」はCのキーワードであることを忘れてください。それを使わないでください。 C++ 11ではその用途が定義されていますが、C99ではノイズです。 –

答えて

0

質問の最初の部分は、constをどこに配置するかに柔軟性があると思いますので、const char **char * const *は同じです。

ご質問の第2部では、ポインタをconstポインタに割り当てることができます。それはエラーを生成する別の方法になります。あなたの明示的なキャストは、コンパイラがポインタの元の型について持っていた知識を削除しました。

+2

いくつかの柔軟性がありますが、 'const char **'(const charへのポインタへのポインタ)は 'char const **'にのみ対応し、 'char * const *'(charへのconstポインタへのポインタ)は異なります。 –

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 1つは~~~~~で下線が引かれています)は実際には必要ではありません:ローカル変数ptr3constであり、指すデータはconstではありません。

次の質問:*(const char * const *) ptr1はなぜですか?

タイプ012を使用している場合、*(const char * const *) ptr1のタイプはconst char * constまたは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 stringへのポインタへのconst voidへのポインタをキャストしているので、いくつかのコンパイラは、あなたが修飾子を破棄され、警告を与えることができます。 C++コンパイラはconst_castconst void *からstring *(a.k.a const char *const *)に変換することを要求するので、そのようなコードを渡すべきではありません。

ただし、逆変換は問題ありません。非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キーワードは廃止されました。これは、最近の何も意味しません。デフォルトのストレージクラス指定子です。 autoは使用しないでください。

+0

徹底的な返答をありがとう。しかし、私が頭を包み込むのに困っていることが1つあります。あなたは言った: "非const文字列へのポインタにconst voidへのポインタをキャストしているので、いくつかのコンパイラはあなたが修飾子を破棄しているという警告を与えるかもしれません。私が混乱しているのは、const void * ptrはconstデータを指すvoidポインタです。しかし(string *)はconstデータへのポインタである(const char *)と同じです。なぜコンパイラはこれに不平を言うでしょうか?あたかもvoidポインタがconstデータを指すconstポインタであるかのように聞こえます。しかし、それは正しいとは言えません。だからなぜキャスト? – user678392

+0

いいえ、 'string *'は 'const char *'と同じではなく、 'const char **'と同じです。だからこそ私はtypedefを使いました。 constへのポインタである 'const void *'をとり、非constへのポインタである 'string *'にキャストすると、コンパイラによっては不平を言います。 –

関連する問題