char** argv または char* argv[] を使用する必要がありますか?

あなたは C を学んでいるので、違いを本当に理解しようとすることをお勧めします。 common の代わりに最初に配列とポインタの間 もの。

パラメータと配列の領域には、先に進む前に明確にしておくべきいくつかの紛らわしい規則があります。まず、パラメータ リストで宣言する内容は特別に扱われます。 C の関数パラメーターとして意味をなさないような状況があります。これらは

  • パラメータとして機能
  • パラメータとしての配列

パラメータとしての配列

2番目の可能性はすぐにはわかりません。しかし、配列の次元のサイズが C の型の一部であると考えると、明らかになります (そして、次元のサイズが指定されていない配列は不完全な型です)。したがって、配列を値で受け取る (コピーを受け取る) 関数を作成する場合、その関数は 1 つのサイズでしか実行できません!さらに、配列は大きくなる可能性があり、C は可能な限り高速にしようとします。

C では、これらの理由から、配列値 存在しません。配列の値を取得したい場合、代わりに取得するのは、その配列の最初の要素へのポインターです。そして、ここに実際にすでに解決策があります。無効な配列パラメータを前もって描画する代わりに、C コンパイラは変換します。 ポインタであるそれぞれのパラメータの型。これを覚えておいてください、それは非常に重要です。パラメータは配列ではなく、それぞれの要素タイプへのポインタになります。

ここで、配列を渡そうとすると、代わりに渡されるのは配列の最初の要素へのポインターです。

エクスカーション:パラメータとして機能

最後に、これで理解が深まると思いますので、パラメータとして関数を持たせようとしたときの様子を見てみましょう。確かに、最初は意味がありません。どのようにパラメーターを関数にすることができますか?もちろん、その場所に変数が必要です。その場合にコンパイラが行うことは、やはり 変換 です。 関数を 関数ポインタ に .関数を渡そうとすると、代わりにそのそれぞれの関数へのポインターが渡されます。したがって、以下は同じです (配列の例に類似):

void f(void g(void));
void f(void (*g)(void));

*g を括弧で囲むことに注意してください が必要です。それ以外の場合は、void* を返す関数を指定します。 void を返す関数へのポインターの代わりに .

配列に戻る

さて、最初に、配列は不完全な型を持つことができると言いました.これは、まだサイズを指定していない場合に発生します.配列パラメーターは存在しないが、代わりに配列パラメーターはポインターであることが既にわかっているため、配列のサイズは問題ではありません。つまり、コンパイラは次のすべてを変換しますが、すべて同じものです:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

もちろん、どんなサイズでも入れられるのはあまり意味がなく、そのまま捨てられてしまいます。そのため、C99 はこれらの数字に新しい意味を与え、括弧内に他のものを表示できるようにしました:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

最後の 2 行は、関数内で "argv" を変更できないことを示しています。これは const ポインターになっています。ただし、これらの C99 機能をサポートする C コンパイラはごくわずかです。しかし、これらの機能は、「配列」が実際には 1 つではないことを明らかにしています。ポインターです。

注意事項

上記のすべては、パラメータとして配列を取得した場合にのみ当てはまることに注意してください 関数の。ローカル配列を扱う場合、配列はポインターにはなりません。 動作 前に説明したように、配列はその値が読み取られるときにポインターに変換されるためです。ただし、ポインターと混同しないでください。

古典的な例の 1 つを次に示します。

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;

どちらでも使用できます。それらは完全に同等です。 litb のコメントと彼の回答を参照してください。

どのように使用するかによって異なります (いずれの場合でも使用できます):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

どちらがより一般的かについては、問題ではありません。あなたのコードを読む経験豊富な C プログラマーは、(適切な条件下で) どちらも交換可能であると考えるでしょう。経験豊富な英語話者が「they're」と「they are」を同じように簡単に読むように.

もっと重要なのは、それらを読んで、それらがどれほど似ているかを認識することです.書くよりも多くのコードを読むことになり、両方に等しく慣れる必要があります。


C 配列とポインターは関数パラメーター リストで交換可能であるため、2 つの形式のいずれかを使用できます。 http://en.wikipedia.org/wiki/C_(programming_language)#Array-pointer_interchangeability を参照してください。