ポインタの仕組みを見ていこう
最近数年前に勉強したC言語を使ってコーディングする機会があったのですが、、ポインタの知識のあたりは使わないと忘却の彼方へ。。。
初投稿といえば!やっぱりポインタでしょ!笑
アウトプットの大切さに最近気付いたので、大切なのに忘れがちな知識をまとめておこうと思います。
アドレスについて
C言語において、メモリの場所を直接表すために使われる「アドレス」というものがある。
その名の通り、コンピュータ内の住所のようなもので、16進数で表されている。
変数のアドレスを知るには、変数名にアドレス演算子である「&」をつける。
変数のアドレスを出力する際には変換仕様として、「%p」を使う。
printf("変数aのアドレスは%pです。\n",&a);
アドレス?PCとか実行状況によって変化するし、そんなもの知ってどうするの?
って感じるかも。
アドレスを使うと、実際の値ではなく、メモリ上の位置がわかる
これの利便性は、後々使用例を挙げるので、そこで実感できる。
ポインタ変数について
ポインタ変数とは、アドレスを格納する特殊な変数である!!
実際に使い方を見てみよう
ポインタ変数の宣言は、普通の変数同様に、型とポインタ変数名の宣言が必要である。
型名 *ポインタ変数名;
int *pA;
ここで、ポインタ変数の宣言には変数名の前に必ず*
という記号をつけることに注意!
逆に、このマークが無ければ自分でもただの変数と見分けがつかなくなりそう^^
また、このポインタ変数に、例えば変数aのアドレスを格納しようと思ったら、以下のようにする。
pA = &a; /*変数aのアドレスをpAに格納、つまり指定する変数の前に&をつける。*/
ポインタ変数pAに格納されたアドレス値を出力する際には、%p
で指定。
printf("変数aの値は%dです。¥n",a);
printf("ポインタ変数pAの値は%pです。¥n",pA);
printf("*pAの値は%dです¥n",*pA);
先ほど同様に宣言すれば、ポインタ変数に別のアドレスを代入することも可能。
ポインタ変数を使って変数の値を変更する
Cのプログラムの一部を抜粋します。
int a;
int *pA;
a = 5;
pA = &a;
printf("変数aの値は%dです。¥n",a);
*pA = 50;
printf("*pAに50を代入しました。¥n");
printf("変数aの値は%dです。¥n",a);
return 0;
実行結果は、、
変数aの値は5です。
*pAに50を代入しました。
変数aの値は50です。
つまり、この結果から何が分かったかというと、*pAとaが同じものを指しているということ。
それでは何が違うかを軽く解釈していく。
変数aの値に数値を代入して値を設定する、というのはプログラミングを学び始めた時に習うやつ。
ポインタ変数に関しては、*pAに値を代入するということは、ポインタ変数pAが指している変数aに値を代入するという解釈であることを覚えておきたい。
このように、ポインタ変数を使うと変数aそのものをいじらなくても、中身が変更できることが分かった。
引数にポインタ変数を使ってみる
swap関数を使ってみる。
void swap(int x, int y){
int tmp; /*一時保管場所としての変数tmpを設定*/
tmp=x;
x=y;
y=tmp;
}
以上のような、引数x,yを交換するswap関数を作り、main関数のなかで使う。
/*main関数の中身を抜粋*/
int num1=1;
int num2=2;
swap(num1,num2);
printf("変数num1の値は%d,変数num2の値は%dです。¥n",num1,num2);
このようにして、「変数num1の値は2,変数num2の値は1です。」と表示されることを期待して実行する。
(・・;)
「変数num1の値は2,変数num2の値は1です。」って出てくる。
swap関数では、仮引数であるxとyが交換されただけであった。1や2といった、実際の数値は、変数num1,num2の中身であり、その中身にまではいじれなかったのである。数値が変数num1,num2の箱の中にある、というイメージを持てば理解できるのではないか。
今回この関数では、箱の外見ごと交換されただけなのである。
じゃあ、どうしたらいいか。
swap関数の仮引数にポインタ変数を使おう!
void swap(int *px, int *py){ /*swap関数の仮引数にポインタ変数を使用する*/
int tmp; /*一時保管場所としての変数tmpを設定*/
tmp=*px;
*px=*py;
*py=tmp;
}
関数内では*を使って、ポインタ変数が指し示しているもの同士を交換しているので、swap関数の内容は先ほどと同じ。
何が違っているかというと、関数を呼び出す際に変数のアドレスが引数として渡されるということである。
swap(&num1,&num2); /*変数num1とnum2のアドレスを渡して関数呼び出しをする*/
このように、仮引数に、変数の値が格納されているアドレスを指すポインタ変数を指定することで、実引数を変更することができる。
(豆知識)
ポインタ変数を引数に使用するからといって、必ずしも実引数を変更する必要はない
関数宣言の際に、仮引数の前にconstをつけるだけで、関数内で実引数の変更ができなくなる。
void func(const int *px){ /*func関数の中身*/
}
(余談)実引数と仮引数
関数の本体で定義されている引数(変数)が仮引数
関数を呼び出す際に渡す値が実引数
void buy(int x){ /*このときのxが仮引数*/
...}
int main(void){
buy(20); /*この20が実引数*/
}
以上です。次は配列とポインタについてまとめていこうかな
記法やコンテンツに関して何かアドバイスや意見がありましたら、教えてください:)
精進します:)
参考:やさしいC (出版社 Softbank Creative)