「C言語の特徴は何ですか?」と聞かれたら、僕はこう答えます。
「ポインタです。」と.....
というわけで、今回はC言語のポインタについて復習しようと思います。
何十年も前に学んだ知識なので、色々と間違っていたらごめんなさい。
今回、僕が学んだラインナップは以下の通り。
1.ポインタとは
2.実践ポインタ
3.実践ポインタのポインタ
4.ポインタを関数へ渡す
5.配列でポインタを使う
ポインタとは
プログラムで使用するデータの物理的な格納位置を示すもの。
だと、僕は思っています。
プログラムで使用するデータは、値や変数、関数、配列...etcを指していて、
それらを物理的に格納する(メモリ)の位置を示すもの。
それがポインタなのかなぁと思いました。
では、実際にポインタを使ってみましょう。
実践ポインタ
int型の変数とポインタ型の変数を宣言して、int型のポインタをポインタ型変数に格納するプログラムを書いてみました。
int inp1; //int型の変数 int *pnt_inp1; //ポインタ型の変数 inp1 = 12; //inp1 の値 printf("value of inp1 = %d\n", inp1); //inp1 のポインタ値 printf("adress of inp1 = %p\n", &inp1); //inp1のポインタ値をポインタ型の変数へ格納 pnt_inp1 = &inp1; //pnt_inp1 の値 printf("value of pnt_inp1 = %p\n", pnt_inp1); //pnt_inp1 のポインタ値 printf("adress of pnt_inp1 = %p\n", &pnt_inp1); //*pnt_inp1 の値 printf("value of *pnt_inp1 = %d\n", *pnt_inp1);
■実行結果
adress of inp1 = 0x7fff593fbb08 value of inp1 = 12 value of pnt_inp1 = 0x7fff593fbb08 adress of pnt_inp1 = 0x7fff593fbb00 value of *pnt_inp1 = 12
順に見ていくと、inp1は"12"という値を持っています。
同時に"12"という値を格納するためのadress(メモリの位置のこと。つまり、ポインタです。)も持っています。
これをポインタ変数pnt_inp1に格納します。
ポインタ変数は、ポインタを格納するための変数です。
もちろん、ポインタ変数にもadressがあります。
最後に、ポインタ変数を参照すると"12"が表示されます。
これはポインタ変数が格納しているpnt_inp1のポインタの値"12"が参照できるためです。
図で書くと以下のような感じでしょうか。
実践ポインタのポインタ
次に、ポインタのポインタについて復習します。
プログラムは上記の続きです。
int **pnt_inp11; //ポインタのポインタ型の変数 pnt_inp11 = &pnt_inp1; //ポインタのadressをポインタのポインタ型の変数へ格納 //pnt_inp11 の値 printf("value of pnt_inp11 = %p\n", pnt_inp11); //pnt_inp11 の値 printf("adress of pnt_inp11 = %p\n", &pnt_inp11); //*pnt_inp11 の値 printf("value of **pnt_inp11 = %d\n", **pnt_inp11);
■実行結果
value of pnt_inp11 = 0x7fff593fbb00 adress of pnt_inp11 = 0x7fff5ae5daf8 value of **pnt_inp11 = 12
こちらは簡単ですね。
ポインタ変数にもポインタという値を格納するためのadressがあるため、そのadressをポインタのポインタ型の変数に格納しただけです。
あとはポインタの時と同様、ポインタのポインタ変数を参照すると"12"が表示されます。
図で書くと以下のような感じです。
しかし、ポインタはただ値を参照するだけのものではありません。
ポインタやポインタのポインタからでも参照先の値を変えることができます。
今度は、試しにポインタのポインタから参照先の"12"を"15"に値を変えてみます。
**pnt_inp11 = 15; //*pnt_inp11 の値 printf("value of **pnt_inp11 = %d\n", **pnt_inp11); //inp1 の値 printf("value of inp1 = %d\n", inp1);
■実行結果
value of **pnt_inp11 = 15 value of inp1 = 15
値が変わりました。
ポインタからでも値の変更ができることが分かりました。
ポインタを関数へ渡す
次は、関数にポインタ変数を渡した後、関数側でポインタの参照先の値を変えてみましょう。
inp2 = 20; //inp2を用意 pnt_swap(pnt_inp11, &inp2); //*pnt_inp11 の値 printf("value of **pnt_inp11 = %d\n", **pnt_inp11); //inp2 の値 printf("value of inp1 = %d\n", inp2); //pnt_swap関数を用意 void pnt_swap(int **pnt_inp11,int *inp2){ int swap_tmp; swap_tmp = **pnt_inp11; //swap_tmpに**pnt_inp11の値を格納 **pnt_inp11 = *inp2; //**pnt_inp11に*inp2の値を格納 *inp2 = swap_tmp; //*inp2にswap_tmpの値を格納 }
■実行結果
value of **pnt_inp11 = 20 value of inp1 = 15
関数側でも値の変更ができました。
配列でポインタを使う
最後に、配列でポインタを使ってみます。
int inp_array[] = {1, 50, 100, 150}; //配列の値 int count; int *pnt_array; for(count = 0; count < 4; count++){ printf("adress of inp_array[%d] = %p ", count, &inp_array[count]); //配列に格納した値毎のポインタ printf("value of inp_array[%d] = %d\n", count, inp_array[count]); //配列に格納した値 } pnt_array = inp_array; //配列をポインタ変数に格納 ※注)この時、ポインタ変数には配列の先頭のポインタ値が格納されます。 for(count = 0; count < 4; count++){ printf("pnt_array[%d] = %d\n", count, *pnt_array); ++pnt_array; //ポインタ変数は、こうやってカウントアップします。 }
■実行結果
adress of inp_array[0] = 0x7fff5a04eb50 value of inp_array[0] = 1 adress of inp_array[1] = 0x7fff5a04eb54 value of inp_array[1] = 50 adress of inp_array[2] = 0x7fff5a04eb58 value of inp_array[2] = 100 adress of inp_array[3] = 0x7fff5a04eb5c value of inp_array[3] = 150 pnt_array[0] = 1 pnt_array[1] = 50 pnt_array[2] = 100 pnt_array[3] = 150
配列をポインタ変数に格納できました。
ポインタ変数をカウントアップすれば、配列に格納された値も取得できる事がわかります。
しかし、ポインタ変数だと配列の特定の位置の値の取得が難しそうです。
例えば、「配列の3番目の値が欲しい」といった場合に、ポインタ変数だと3回カウントアップしてから値を取得しなくてはなりません。
この辺りは何か工夫する方法があるのか.....うーん...