web-dev-qa-db-ja.com

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

私はCを学んでいるだけで、メインメソッドでこれらのどれを使用すべきか疑問に思っていました。違いはありますか?

編集:では、どちらがより一般的ですか?

121
Kredns

Cを学習しているだけなので、まずcommonの代わりに、配列とポインターの間のdifferencesをまず理解することをお勧めしますもの。

パラメータと配列の領域には、続行する前に明確にする必要があるいくつかの混乱を招くルールがあります。まず、パラメーターリストで宣言したものは特別に扱われます。 Cの関数パラメーターとして物事が意味をなさない状況があります。

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

パラメータとしての配列

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

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

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

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

完成のために、そしてこれはあなたが問題をよりよく理解するのに役立つと思うので、パラメーターとして関数を持たせようとするときの情勢がどのようなものか見てみましょう。実際、最初は意味がありません。パラメータを関数にするにはどうすればよいですか?もちろん、その場所に変数が必要です!そのため、コンパイラーは、それが発生したときに、再びtransformfunction pointerに変換します。関数を渡そうとすると、代わりにその関数へのポインタが渡されます。したがって、以下は同じです(配列の例に類似):

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つではないことが明らかになります。ポインタです。

警告の言葉

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

古典的な例の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;
157

どちらでも使用できますが、使用方法によって異なります。 char* argv[]は(ほとんど)char ** argvと同等です。どちらの形式もcharへのポインターへのポインターです。唯一の違いは、char *argv[]を使用すると、argvの値が変更されないことをコンパイラーに通知します(ただし、指す値は変更できます)。 実際、私は間違っています、彼らは完全に同等です。 litb のコメントと his answer を参照してください。

それは本当にあなたがそれをどのように使用したいかに依存します(そしてあなたはいずれの場合でも使用できます):

// 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プログラマは、両方とも交換可能と見なします(適切な条件下)。経験豊富な英語話者が「彼らは」と「彼らは」を同じように簡単に読むように。

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

12
rampion

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

9
lothar

違いはありませんが、char *argv[]は、可変長文字列の固定サイズ配列(通常はchar *)。

8
Zifre

実際には違いはありませんが、後者の方が読みやすくなっています。与えられるのは、2番目のバージョンが言うように、charポインターの配列です。ただし、最初のバージョンのように、暗黙的にdouble charポインターに変換できます。

4
jalf

それをchar *argv[]として宣言する必要があります。これは、それを宣言する多くの同等の方法があるため、直感的な意味に最も近い、文字列の配列です。

1
Andras

char **→文字ポインタへのポインタとchar * argv []は、文字ポインタの配列を意味します。配列の代わりにポインターを使用できるため、両方を使用できます。

1
Alphaneo

私は、他のアプローチの代わりにいずれかのアプローチを使用することの特別なメリットはないと思います-あなたのコードの残りの部分と最も一致する規則を使用してください。

0
Christoffer