*argv[] **argvとはなんぞや?

概要

今回はCS50 week2の課題 substitutionを解いているときに発見したことをシェアします。

https://cs50.harvard.edu/college/2020/spring/psets/2/substitution/

課題の内容は以下の通り。コマンドライン引数で与えられたkeyに応じて文章を暗号化するアルゴリズムです。

1. ユーザーからkeyをコマンドライン引数として受け取る。
2. keyがアルファベット26文字を全て1文字ずつ含むように、keyの長さとアルファベット以外を含むかどうかをチェックする。
(例)有効なkey -> NQXPOMAFTRHLZGECYJIUWSKDVB
(大文字小文字どちらでもよい)
3. ユーザーに暗号化したい文章を入力させる。
4. 入力された文章のアルファベットを一文字ずつそれぞれ、keyに対応させて暗号化する。
(例)A or a -> N or n, B or b -> Q or n, C c -> X or x, ... 

実装

まずはkeyの長さとアルファベット以外を含むかどうかを調べるところから。
必然的にユーザーによって入力された文字列が必要になります。
どうやらコマンドライン引数はmain関数に渡され、argvという配列に引数が入っているらしいので、なんとなく

char key = argv[1]

とやってみるとエラー。
argv[1]はポインタらしく、char型に代入はできないとのこと。

よくよく調べてみると

int main(int argc, *argv[]);
int main(int argc, **argv);

というふうに、配列へのポインタもしくはダブルポインタとしてmain関数に渡されている。
main関数に渡されたコマンドライン引数は文字列としてargvという「文字列の配列」に保存されるようです。

C言語における配列と文字列

どうやらargvは「文字列の配列」らしい。

ここで配列というのは、メモリ上に連続して保存された複数の値のまとまりのことです。
つまりargv 文字列の配列というのは、シンプルに文字列がたくさん集まったものという意味になります。

そしてC言語において文字列とは「文字の配列」のことでした。
先ほどと同じように文字列というのは、文字がたくさん集まったものに過ぎないわけですね。

以上の2つを組み合わせて考えてみますと、

argvは文字列の配列である。
そして文字列は文字の配列である。
よってargvは文字の配列の配列である。

つまりargvは「文字をたくさん集めたものをさらにたくさん集めたもの」だということが分かりました。
これがわかれば、なぜargvがダブルポインタなのかもはっきりします。

**argvの正体

配列というのは、配列の最初の要素のアドレスでやりとりするものでした。
なぜなら配列を定義した時点で、配列の中に入っている要素はメモリ上に連続して保存されているということが保証されているので、先頭要素のアドレスさえわかれば事足りるからからです。

分かりやすいように以下のように考えましょう。

char **argv == {"arg0", "arg1"}



argvの最初の要素(arg0)にはプログラムの名前が文字列として入っています。
例えばプログラムの名前がa.outであれば、

arg0 [] = "./a.out"

となっているわけです。
2つ目の要素(arg1)には入力したコマンドライン引数の1つ目が文字列として入っています。



argvは配列ですから、変数argvにはargvの配列の中の一番最初の要素(文字列)のアドレスが入っています。
つまりarg0のアドレス(&arg0)ですね。

argv == &arg0;



ところがarg0もまた文字の配列つまり文字列です。
したがって変数arg0にもまたarg0の配列の中の一番最初の要素(文字)のアドレスが入っています。
つまり
arg0[0] == '.'
のアドレス(&arg0[0])ですね。

arg0 == &arg0[0];



したがってargvというのはアドレスのアドレスを示す変数であるということが分かります。
アドレスを保存するための変数をポインタというのなら、アドレスのアドレスを保存する変数はポインタのポインタとなるわけです。

結論

以上のことから、変数argvは文字列の配列つまり文字のアドレスのアドレスを保存する変数なので、定義するときはアスタリスクを2つつける必要があることが分かります。
ということでコマンドライン引数をmain関数で受け取るときは、

int main(int argc, char **argv);

もしくは

int main(int argc, char *argv[]);

と書くことになるわけです。

こういうところがC言語は難しいと言われる所以なのかもしれませんね。

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした