web-dev-qa-db-ja.com

C / C ++のメインargvが、単に「char * argv」ではなく「char * argv []」として宣言されるのはなぜですか?

argvが単に「配列の最初のインデックスへのポインタ」ではなく、「配列の最初のインデックスへのポインタへのポインタ」として宣言されているのはなぜですか(char* argv)?

ここで「ポインターツーポインター」の概念が必要なのはなぜですか。

21
a user

Argvは基本的に次のようなものです。

enter image description here

左側は引数自体です。実際にはmainに引数として渡されます。これには、ポインタの配列のアドレスが含まれています。それらのそれぞれは、コマンドラインで渡された対応する引数のテキストを含むメモリ内のいくつかの場所を指します。次に、その配列の最後にnullポインターがあることが保証されます。

個々の引数の実際のストレージは、少なくとも互いに個別に割り当てられる可能性があるため、メモリ内のアドレスはかなりランダムに配置される可能性があることに注意してください(ただし、書き込みがどのように行われるかによっては、単一の連続したブロックに配置される場合もあります)メモリ-あなたは単に知らないし、気にする必要はありません)。

60
Jerry Coffin

それがオペレーティングシステムが提供するものだからです:-)

あなたの質問は、鶏/卵の反転問題の少しです。問題は、C++で必要なものを選択することではなく、問題は、C++でOSが提供するものをどのように言うかです。

Unixは「文字列」の配列を渡します。各文字列はコマンド引数です。 C/C++では、文字列は「char *」なので、文字列の配列はchar * argv []、またはchar ** argvです。

23
passer-by

まず、パラメータ宣言として、char **argvchar *argv[]と同じです。どちらも、文字列へのポインタ(1つ以上の可能な配列またはセット)へのポインタを意味します。

次に、 "charへのポインタ"しかない場合—たとえばchar *のみ— n番目のアイテムにアクセスするには、最初のn-1アイテムをスキャンして、n番目のアイテムの開始点を見つける必要があります。 (これはまた、各文字列が連続して格納されるという要件を課します。)

ポインタの配列を使用すると、n番目の項目に直接インデックスを付けることができます—したがって、(厳密には必要ではありませんが、文字列が連続していると仮定して)、通常ははるかに便利です。

説明する:

./program hello world

argc = 3
argv[0] --> "./program\0"
argv[1] --> "hello\0"
argv[2] --> "world\0"

OSが提供する文字の配列では、次のことが可能です。

            "./program\0hello\0world\0"
argv[0]      ^
argv[1]                 ^
argv[2]                        ^

argvが単なる「charへのポインタ」の場合、

       "./program\0hello\0world\0"
argv    ^

ただし(おそらくOSの設計による)、3つの文字列「./program」、「hello」、および「world」が連続しているという実際の保証はありません。さらに、この種類の「複数の連続した文字列への単一のポインタ」は、特に文字列へのポインタの配列と比較すると、(Cの場合)より珍しいデータ型構成です。

15
Erik Eidt

C/C++メインargvが「char * argv []」として宣言される理由

考えられる答えは、C11標準n157§5.1.2.2.1プログラムの起動 =)およびC++ 11標準n3337 (in§3.6.1メイン関数ホストされた環境ではが必要です(ただし、C標準では にも言及していることに注意してください)§ 5.1.2.1独立型環境this も参照してください。

次の質問は、CおよびC++標準がmainを選択してそのようなint main(int argc, char**argv)シグネチャを持つようにしたのはなぜですか?説明は主に歴史的です: [〜#〜] c [〜#〜]nix で発明されました Shell を持っています- globbing を実行する前に fork (これはプロセスを作成するシステムコールです)および execve (これはプログラムを実行するためのシステムコール)、およびそのexecveは文字列プログラム引数の配列を送信し、実行されたプログラムのmainに関連しています。 nixの哲学[〜#〜] abi [〜#〜] sの詳細をご覧ください。

そして、C++はCの規則に従い、互換性を持つように懸命に努力しました。 mainをCの伝統と互換性がないように定義することはできませんでした。

オペレーティングシステムを最初から設計し(まだコマンドラインインターフェイスを備えています)、そのためのプログラミング言語を最初から設計している場合は、さまざまなプログラム開始規則を自由に作成できます。また、他のプログラミング言語(Common LISP、Ocaml、Goなど)では、プログラムの開始規則が異なります。

実際には、mainはいくつかの crt コードによって呼び出されます。 Windowsでは、グロビングはcrt0と同等のプログラムごとに行われる場合があり、一部のWindowsプログラムは非標準の WinMainエントリポイント から開始できることに注意してください。 Unixでは、グロビングはシェル(およびcrt0は、ABI、およびABIが指定した初期コールスタックレイアウトを、C実装の呼び出し規約に適合させています。

13

「ポインタからポインタ」と考えるのではなく、「[文字列の配列]」と考えると役立ちます。[]は配列を示し、char*は文字列を示します。プログラムを実行するとき、1つ以上のコマンドライン引数をプログラムに渡すことができ、これらはmainへの引数に反映されます。argcは引数の数であり、argv個々の引数にアクセスできます。

12
casablanca

多くの場合、答えは「標準だから」です。引用するには C99標準

— argcの値がゼロより大きい場合、arrayメンバーargv [0]からargv [argc-1]までの範囲に文字列へのポインター。プログラムの起動前にホスト環境によって実装定義の値が与えられます。

もちろん、標準化される前は、初期のUnix実装でK&R Cによってすでに使用されていて、コマンドラインパラメータ(/bin/bashまたは/bin/shですが、組み込みシステムにはありません)。 f K&Rの「Cプログラミング言語」の初版(110ページ) を引用するには:

最初の(慣習的にargcと呼ばれる)は、プログラムが呼び出されたコマンドライン引数の数です。 2番目の(argv)は、文字列ごとに1つ、引数を含む文字列の配列へのポインタです。

1