はじめに
ポインタ渡し・ポインタ演算の復習というか勉強のためにいろいろ書いて試したことがあるので,それを公開しておきます.
自分の勉強ノートとしてと,初心者向けに「こう書くとこうなる」の例を紹介できればという記事です.
一連の関数へのポインタ渡しの話の最後の記事という位置付けでもあります.
なお,以下の説明にはあまり自信がないので,鵜呑みにされるとまずいかも知れないですし,よく分かってらっしゃる方に「合ってる」「間違ってる」等コメントいただけると幸いです.
まず変数のポインタ渡しをしてみた
まずは簡単と思われる方から.配列をあとでやります.
書いてみたコードはこれです.
#include <stdio.h>
// int型変数のアドレスを受ける
void func1(int* pt){
*pt = 5; // ポインタが指す先の変数の中身を5に
}
// int型ポインタのアドレスを受ける
void func2(int** pt){
**pt = 6; // ポインタが指す先のポインタが指す先の変数の中身を6に
}
int main(void){
int a = 0;
func1(&a); // 変数のアドレスを渡す
printf("call func1(&a)\n");
printf("a=%d\n\n",a);
int* b = &a;
func2(&b); // 変数のアドレスを格納したポインタのアドレスを渡す
printf("call func2(&b)\n");
printf("a=%d *b=%d\n\n", a, *b);
func1(b); // 変数のアドレスを格納したポインタを渡す
printf("call func1(b)\n");
printf("a=%d *b=%d\n\n", a, *b);
return 0;
}
$ ./sample1
call func1(&a)
a=5
call func2(&b)
a=6 *b=6
call func1(b)
a=5 *b=5
コードとコメントを見てもらえればだいたいわかってもらえるでしょうか.
main()
内の最初のfunc1()
にはpt
に変数a
のアドレスを渡していて,func()
内で*pt
と書くことで変数a
の中身を操作できます.
func2()
では,pt
がポインタb
のアドレスを格納し,ポインタb
が変数a
のアドレスを格納しているので,*pt
でb
の中身を,**pt
でa
の中身を操作できます.
最後のfunc1()
にはポインタb
を渡すことでb
が格納しているa
のアドレスを渡しています.
次に配列をポインタ渡ししてみた,そしてポインタ演算もしてみた
配列についてはこんなコードを試してみました.
#include <stdio.h>
// char型変数のアドレスを受ける
void func1(char* pt){
pt[0] = '1';
pt[1] = '2';
pt[2] = '3';
}
// char型ポインタのアドレスを受ける
void func2(char** pt){
// ポインタ演算
**pt = '4';
*(*pt + 1) = '5';
*(*pt + 2) = '6';
}
// char型ポインタのアドレスを受ける
void func3(char** pt){
// 添え字
(*pt)[0] = '7';
(*pt)[1] = '8';
(*pt)[2] = '9';
}
int main(void){
char a[4] = "000";
func1(a); // 配列の先頭アドレスを渡す
printf("call func1(a)\n");
printf("a=%s\n\n",a);
char* b = a;
func2(&b); // 配列の先頭アドレスを格納したポインタのアドレスを渡す
printf("call func2(&b)\n");
printf("a=%s b=%s\n\n", a, b);
func1(b); // 配列の先頭アドレスを格納したポインタを渡す
printf("call func1(b)\n");
printf("a=%s b=%s\n\n", a, b);
func3(&b); // 配列の先頭アドレスを格納したポインタのアドレスを渡す
printf("call func3(&b)\n");
printf("a=%s b=%s\n\n", a, b);
return 0;
}
$ ./sample2
call func1(a)
a=123
call func2(&b)
a=456 b=456
call func1(b)
a=123 b=123
call func3(&b)
a=789 b=789
今度は配列なのでa
はchar型配列の先頭アドレスになります.
なのでポインタに代入する際,先ほどはb = &a
でしたが,今度はb = a
になっています.
コードとコメントから「こう書くとこうなる」を感じ取ってもらえるでしょうか.
ちなみに,func2()
,func3()
内の
*(*pt + 1) = '5';
や
(*pt)[1] = '8';
の()
を書かないと,前者はコンパイル時にエラーになり,後者は実行時にコアダンプします.
演算に優先順位があり,それが変わってしまうからです.
()
を書かなかった場合の優先順位を()
で表現するとおそらくこうです.
(**pt) + 1 = '5'; // 代入する式になっていない
*(pt[1]) = '8'; // ptに2番目の要素はない
func3()
のpt
について,添え字が0
の*pt[0]
だけは()
無しでも大丈夫です.
まとめ
ポインタについていろいろな例を見てきました.
何かしら理解が深まったり発見があったりすれば幸いです.
ちなみに,ポインタ型の宣言はint* b;
とint *b;
の2通りの書き方がありますが,僕は前者が好きです.
以前は後者で書いていたのですが,どうも間接演算子の*
(*pt = 5
とかの*
)と混同して覚えてしまっているような気がして,それからは前者で書いて自分に別物だと言い聞かせています.どちらで書いても構いませんが,別物だということを覚えておいてください.