【配列とポインタ】第5回 char **argvを実装してみる(1)

main()の引数である、「char **argv」の擬似変数「char **argv2」を実装してみます。

  1. 引数文字列の実体をスタックの連続領域に作成し、
  2. 各文字列の位置情報を管理するポインタ配列を作成し、
  3. ポインタ配列の位置情報をargv2に格納する

という流れでやっていきます。

まず、引数文字列の実体の作成から。

char string_0[] = "aaa";
char string_1[] = "bbb";
char string_2[] = "ccc";

これで、引数文字列の実体が、メモリ上に作成できました。

location data type
00000000 "aaa\0" char[]
00000004 "bbb\0" char[]
00000008 "ccc\0" char[]

ちなみに、これらの文字列の実体が、アドレスの昇順にかつ宣言順にスタックの連続領域に格納されることが保証されていれば、例えば「*(&arr_0+1)」のような式で、隣の"bbb"を参照できます(移植性が低下するのでやるべきではありませんが)。

つぎに、これら文字列のアドレスを管理する、ポインタ配列を作成します。

char *arr_list[4];

arg_list[0] = string_0;
arg_list[1] = string_1;
arg_list[2] = string_2;
arg_list[3] = NULL;
location data type
AAAAAAA0 00000000 char *
AAAAAAA4 00000004 char *
AAAAAAA8 00000008 char *

注意したいのは、ポインタ配列の各要素が格納しているのは、配列へのポインタではなく「配列の先頭要素へのポインタ」ということです。でないと、char **argvに代入することができません。

以下に、参照関係を示します。

location data type location data type
AAAAAAA0 00000000 char * ===> 00000000 "aaa\0" char []
AAAAAAA4 00000004 char * ===> 00000004 "bbb\0" char []
AAAAAAA8 00000008 char * ===> 00000008 "ccc\0" char []

ここまでで、ポインタ配列arg_listをメモリ上に格納したわけですが、本物のargvと同じように、arg_listの先頭要素へのポインタ(&arg_list[0])がメモリ上に格納されるのも再現します。

char **argv2;

argv2 = arg_list;
location data type
FFFFFFF0 AAAAAAA0 char **

以下に、全体の参照関係を示します。

addr data type location data type location data type
FFFFFFF0 AAAAAAA0 char ** ===> AAAAAAA0 00000000 char * ===> 00000000 "aaa\0" char []
AAAAAAA4 00000004 char * ===> 00000004 "bbb\0" char []
AAAAAAA8 00000008 char * ===> 00000008 "ccc\0" char []

ちなみに、以下のように一発で書くこともできますが、ただ、これだと文字列の実体がスタックに積まれずに.rodataに作成されるので、本物とはちょっと違うものになってしまいます。

  1     char *arg_list[4] = {"aaa","bbb","ccc",NULL};
  2     char **argv2 = arg_list;

今回は、説明のために二次元配列を使わずにベタなコーディングをしていますが、次回は二次元配列とループを使った冗長性のない書き方をしてみたいと思います。