C言語のリファレンスの読み方

C言語は、ポインタ周りを深く理解していないと、リファレンスを読むことすらできません。
今回は、その解説を行っていきたいと思います。
問題のページは、こちら
この中から

int execvp(const char *file, char *const argv[]);  

について解説して行きたいと思います。

  • 定石その1 : constは無視
    constというのは、そのまま「静的な」という意味なのですが、これは、定数宣言の時にも用いられ、ここでは、「関数内で書き換えられることはありませんよ」ということを僕らに教えてくれてるだけなのです。なので、このconstについては意識しなくても、リファレンスの理解は十分可能です。
    すると、こうなります。
int execvp(char *file, char * argv[]);
  • 定石その2 : あすたりすくの位置
    ポインタを表す「*」という記号ですが、これは、変数から離しましょう。
    すると、こうなります。
int execvp(char* file, char* argv[]);
  • 定石その3 : そのまま変数に定義されている型のデータを入れる
    char*型とは、1byteの部屋をもったポインタ型のことです。
    つまり、file変数にはアドレス1(以降p0)があり、そのp0は1byte分の広さがあるということで、
    argv[]配列の中には、それぞれアドレス1,アドレス2,アドレス3,,,(以降p0,p1,p2)があり、それぞれ1byte分の広さがあるということです。

以上の定石3つを適応させると、こうなります。

char *args = {"sh", NULL};
execvp(args[0], args);

char *argsでargsという1byte分の部屋を持つアドレス専用の変数を用意します。
その変数を配列として、{“sh”, NULL}を入れます。
こうすることで、args[0]->p0->“sh”, args[1]->p1->NULLとなります。
これがすんなりわからない方は、普通のポインタ型宣言を思い出すと良いでしょう。
char *pHoge = “hogehoge”;
これは、pHogeという1byteの部屋をもつポインタ変数を用意します。
そのあと、そこに"hogehoge"というデータをもつ先頭番地をpHogeに格納します。(ポインタ型変数宣言時に代入する場合はアドレスでなければならないので、本来はキャストが必要。しかし、警告がでるだけで自動キャストが行われる。)
すると、pHogeにはアドレスが格納され、*pHogeはそのアドレスの中身が格納されます。
この時の"hogehoge"を{“hoge”,“fuga”}という配列の形にすることで、pHogeにはその配列の先頭番地が格納され、*pHogeの中にはその配列の先頭要素が格納されることになります。
つまり、結果的にchar *args = {“sh”, NULL};でargs[0]には"sh"が格納されたアドレス、args[1]にはNULLが格納されたアドレス、argsには配列の先頭番地が格納されることになります。
そのあとにexecvp(args[0], args);でexecvp(“sh"が格納されたアドレス, {"sh"が格納されたアドレスが格納されたアドレス,NULL}); となり、定石3の形と同じにすることができました。
これを実際のメモリ的に考えると、

...  
p0 --> 0x12345678 --> "sh"  
...  
p1+0 --> 0x78  
p1+1 --> 0x56  
p1+2 --> 0x34  
p1+3 --> 0x12  
p1+4 --> 0x00  
p1+5 --> 0x00  
p1+6 --> 0x00  
p1+7 --> 0x00  
...  

この時、
execvp(p0,p1+0)である。