Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

Cのポインタについて復習してみた

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"が参照できるためです。
図で書くと以下のような感じでしょうか。

f:id:special-moucom:20161217095803j:plain

実践ポインタのポインタ

次に、ポインタのポインタについて復習します。
プログラムは上記の続きです。

  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"が表示されます。
図で書くと以下のような感じです。

f:id:special-moucom:20161217095809j:plain

しかし、ポインタはただ値を参照するだけのものではありません。
ポインタやポインタのポインタからでも参照先の値を変えることができます。 今度は、試しにポインタのポインタから参照先の"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回カウントアップしてから値を取得しなくてはなりません。

この辺りは何か工夫する方法があるのか.....うーん...