ついて【おさらいC言語】第2回:メイン関数のargcとargvの形をイメージしよう!全13回でお届けする【おさらいC言語】シリーズ。第2回目のテーマは、C言語のメイン関数がコマンドラインから受け取ることができるパラメータargcとargvの構造についてです。これらの中に何のデータがどのように格納されるのかを、イメージしやすいように解説していきます。ほかのプログラミング言語との違いについても紹介します。文字列の配列とポインタまとめコマンドライン引数(コマンドライン・パラメータ)とはコマンドライン引数とは、ターミナル(コマンドライン)からコマンドやプログラムを起動する時に渡すことができる引数(パラメータ)のことをいいます。ターミナルから実行できるプログラムにはさまざまなものがありますが、その多くではコマンドライン引数としてオプションやファイル名などを与えられるようになっています。前回(第1回)の記事で、ハローワールドのプログラムをターミナルからコンパイルする例を紹介しました。次のようにgccを実行すると、a.outという名前の実行ファイルが作られます。この例では、main.cの部分が引数です。main.c引数が2つ以上あるときは、次のようにスペースで区切って指定します。main.cこの例では-o、main、main.cの3つが引数です。mainという2つの引数の並びで「出力する実行ファイル名を(a.outではなく)mainにする」という指定になります。ワンポイントターミナルから実行できるさまざまなコマンドには、コマンドライン引数を与えられるようになっている。C言語のコマンドライン引数の構造コマンドライン引数は、自分でコンパイルしたプログラムにも与えることが可能です。ここからは、C言語で書いたプログラムを実行したときに、コマンドライン引数がどのような構造で受け取られるのかをみていきましょう。C言語でコマンドライン引数を受け取るには例えば、ハローワールドの実行ファイルa.outを起動するときにabcとdefgの2つの引数を指定したい場合は、ターミナルで次のように入力して実行します。world.しかし、コマンドライン引数を与えても与えなくても、ハローワールドの動作は変わりません。ハローワールドの中で使われているメイン関数は、コマンドライン引数を無視しているからです。メイン関数には2通りのプロトタイプがありますが(詳しくは第1回の記事を参照)、コマンドライン引数を受け取るプログラムを書くには、次のようにパラメータ付きのメイン関数を使う必要があります。argcとargvの2つのパラメータでコマンドライン引数を受け取っている}これで、プログラムが受け取ったコマンドライン引数は、メイン関数のパラメータとしてargcとargvの2つの変数に格納されるようになります。vector)」の略です。配列とは、同じ形式のデータが一列に並んだようなデータ構造のことです。データを配列として格納しておくと、先頭から何番目にあるのか(インデックス)さえわかれば、どのデータにでも素早くアクセスすることができます。そのため、プログラミングでは配列が頻繁に使われています。ワンポイントC言語でコマンドライン引数を受け取るには、パラメータ付きのメイン関数を使う。argcに入っている値C言語よりも新しいプログラミング言語では、配列にはサイズ(配列内のデータの個数)も記憶されているのが一般的です。C言語の配列にはサイズを知る方法が備わっていません。そのため、メイン関数には配列argvに格納されたコマンドライン引数の一覧とともに、そのサイズ情報としてargcも引き渡されるようになっています。argcとなっているので、整数(integer)型です。実際にどのような値が格納されるのかを確認するために、argcの値を表示するプログラムを作ってみましょう。argc);}このprintfでは、1つ目のパラメータでテキストのフォーマットを指定しています。フォーマット中の%dの部分には、2つ目のパラメータとして与えたargcの値が入ります。integer)の意味です。プログラムをコンパイルして、コマンドライン引数を与えて実行してみましょう。3実行してみると、argcにはコマンドライン引数の個数よりも1つ大きい値が入っていることがわかります。コマンドライン引数を指定しない場合はいくつになるか試してみましょう。1このように実行すると、コマンドライン引数がないときのargcは1だということがわかります。ワンポイントC言語のargcには「コマンドライン引数の個数+1」が格納される。次は、argvの中身を確認してみましょう。次のように、argcを表示するプログラムにforループを追加します。*argv[]と少し複雑な形になっていますが、これは文字列の配列だということを表す書き方のひとつです。配列のインデックスは0からはじまるので、最初の文字列にはargv[0]とすればアクセスできます。配列の個数がargcに格納されることから、最後の文字列はargv[argc-1]となります。これを利用すれば、forループでargvに含まれるすべての文字列にアクセスすることが可能です。上のプログラムでは、forループによって変数iの値を0からargc-1まで変化させながらprintfを実行しています。printfのフォーマットに%sという指定が出てきましたが、これは文字列(String)を当てはめるためのものです。iは数値なので%d、argv[i]は文字列なので%sで、うまく表示することができます。コマンドライン引数を与えてプログラムを実行してみましょう。defg実行結果をよく観察すると、argcの値が「コマンドライン引数の個数+1」になっている理由がわかります。argvの最初の要素には、実行ファイル名が入っているのです。そのため、C言語でコマンドライン引数を使ったプログラムを書くときは、argvの2つ目以降の値をチェックすればよいということになります。ワンポイントC言語のargvには実行ファイル名とコマンドライン引数が文字列で格納される。ほかのプログラミング言語との比較ここまでC言語についてみてきましたが、ほかのプログラミング言語でもコマンドライン引数を受け取ることは可能です。例として、ここではJavaとSwiftについて簡単に触れておきます。下記は、コマンドライン引数をすべて表示するプログラムをJavaで書き直したものです。}}C言語と異なり、Javaのメイン関数が受け取るパラメータは文字列の配列のみとなっているのがわかるでしょうか。これは、配列そのものがサイズ情報をもっているのでargcが不要なためです。コンパイル方法などの詳細は省きますが、上記を実行するとC言語とは少し異なる動きをします。メイン関数が受け取るargsにはコマンドライン引数のみが含まれており、実行ファイル名は入っていません。次は、Swiftで書き直したプログラムです。\(args[i])")}Swiftにはメイン関数がありませんが、Foundationと呼ばれる標準機能によってコマンドライン引数を取り出すことができます。こちらもコンパイル方法などは省きますが、上記を実行すると、C言語とよく似た動作をすることがわかるはずです。配列の最初に実行ファイル名が入る点も、C言語と同じです。基本的な概念に大きな違いはないここまでのプログラムを比較してみると、配列の柔軟性やテキストのフォーマット方法、ループの文法などがプログラミング言語によって少しずつ異なることがわかるでしょう。後発の言語はC言語に比べて洗練されていますが、基本的な概念には大きな違いがありません。これは、C言語の知識があれば、ほかの言語も理解しやすいということを意味しています。ワンポイントC言語がわかれば、ほかのプログラミング言語への理解も深まる。文字列の配列を正確にイメージしようここからは、話をC言語に戻します。メイン関数のargvは文字列の配列でした。C言語における文字列の配列とは、どのような構造をしたデータなのかを整理していきます。文字列の配列とは先程、配列とは「同じ形式のデータが一列に並んだような構造」をしていると説明しました。文字列の配列とは、文字列が一列に並んでいる構造だと想像できます。これを図に描いてみるとどのようになるでしょうか。次のような構造をイメージする人が多いかもしれません。上記のようなイメージは概念図としては間違っていませんが、C言語のデータ構造としては正解とはいえません。C言語における文字列の配列とは、より正確には「文字列を指すポインタの配列」だからです。これを踏まえて文字列の配列を表現すると、次の図のようになります。難しくなってきましたので、順を追って説明していきます。「ポインタ」はC言語の学習者がつまずきやすいといわれている箇所ですが、正しいイメージさえできればそれほど難しいものではありません。ワンポイント文字列の配列とは、文字列を指すポインタの配列のこと。文字列の構造しっかりとイメージを固めるために、まずは文字列について理解しましょう。文字列とは、文字がずらっと並んで列になったデータのことです。そのため、「文字の配列」として表すことができます。C言語の言葉でより正確に表現するなら、「ヌル文字で終わるchar型の配列」です。char型というのは文字(character)を格納するための整数のことで、文字のひとつひとつに対応づけられた番号によって、半角の英数字や記号を1文字だけ表現できます。char型は「ヌル(null)」という特別な値をとることもできます。char型の値が配列となって並び、その最後の要素がヌルになっているのが文字列です。図にすると、下記のようなイメージです。ヌル文字は、C言語ではバックスラッシュを使って\0と書くことになっています。日本語環境ではバックスラッシュの部分が半角の円記号(¥)になる場合があります。「null」は「ナル」と書いたほうが実際の発音に近いのですが、慣例的に「ヌル」と書くことが多いようです。ちなみに、nullには「何もない」という意味があります。第1回の記事で出てきたvoidも同じような意味を持つ単語です。nullが「何もないことを示すデータ」であるのに対し、voidは「データすらない」と捉えると違いがわかるでしょう。ワンポイントC言語の文字列とは、終端がヌルになった文字の配列のこと。文字列の配列とポインタ文字列の構造がわかったので、argvのような「文字列の配列」についてあらためて考えてみましょう。文字列が「文字の配列」だということから、「文字列の配列」=「文字の配列の配列」と考えるとどうなるでしょうか。図にすると、下記のようなイメージです。上記は「2次元配列」と呼ばれる構造です。この場合、すべての文字列に同じサイズの領域を用意する必要があります。文字列の長さはバラバラなので、どうしても無駄な領域ができてしまいます。このような無駄をなくすためには、ポインタが便利です。ポインタには、データの「アドレス」を格納することができます。プログラムから直接アクセスできるデータは、intもcharも、それらの配列であってもすべてメモリ上に配置されます。配置するだけでは見失ってしまうので、どこにあるのかがわかるように必ずアドレスが割り振られます。そのため、ポインタにアドレスを格納しておけば、ポインタがデータを指し示すようになり、どのようなデータにでもたどり着くことができます。文字列のアドレスも、ポインタに格納することができます。そのため、「文字列の配列」を「文字列を指すポインタの配列」として表現することが可能です。図にすると、下記のようなイメージです。ポインタを使うことで、無駄な領域がなくなったことがわかるでしょうか。これが、argvの構造です。実際のargvでは最後に0が追加され、要素数がargcより1つ多くなっています。argv[argc]は必ず0になります。ポインタに格納される0は、「どのデータも指し示していない」ということを意味する特別な値です。C言語では、0の代わりにNULLと書くこともできます。0が格納されたポインタは「ヌル(NULL)ポインタ」と呼ばれます。char型に格納できるヌル(\0)と名前が似ていますが、別のものなので区別するようにしましょう。この記事の上のほうでは、コマンドライン引数をすべて表示するプログラムを紹介しました。argvの終端がヌルポインタであることを利用する方法でも、同じようなことが可能です。次のようにすればよいでしょう。}}ワンポイントポインタをうまく使えば、無駄な領域がいらなくなる。まとめC言語のメイン関数がコマンドラインから受け取ることができるパラメータargcとargvには、コマンドライン引数のサイズとその一覧が入っています。ここまで読んで、これらの構造がなんとなくイメージできたのではないでしょうか。ポインタのイメージはまだ少し曖昧な状態かもしれません。正確なイメージをもつには、データやポインタがメモリの中でどうなっているのかを想像できるようになることが大切です。これはC言語だけでなく、いろいろなプログラミング言語への理解を深めるためにも重要な部分だといえます。次回は、メイン関数のパラメータargvがメモリの中でどのようになっているのかについて、詳しく説明していきます。準備中おさらいC言語とは?本シリーズでは、C言語でつまずきがちな箇所を紹介しながらソフトウェアの仕組みやプログラミングの基礎について解説しています。この機会に、C言語をおさらいしてみてはいかがでしょうか。