C++ 仮引数・実引数に関係してくるものを色々書きながら覚えるための初心者の自己中記事
関数を作成する場合に
------------------------------------
void Test(int x){ //カッコ内が仮引数
cout << x << endl;
}
int main(){
Test(10); //カッコ内が実引数
}
(結果)
10
------------------------------------
の場合は
10
になる。
ここで勘違いしないように気をつける。
Test(10);
の10が
void Test ( int x ) {
の int x に移動したように感じるが、これはint x が宣言されている。
Test(10);で関数が呼ばれた時に関数本体(実装部分?)がint xを宣言する。
その時にint xは初期化が行われている。
初期化を勝手に行うのはコンストラクタと言われるものでこの場合は
int x = 10;
と言う動きがあるようだ。
この為Test関数内でxが10として使用できる。
これを踏まえて
--------------------------------------
void Test(int x){
cout << "Test関数内 x : " << x << endl;
x -= 5;
cout << "Test関数内 x : " << x << endl;
}
int main(){
int i = 10;
Test(i);
cout << "main内 i : " << i << endl;
}
(結果)
Test関数内 x : 10
Test関数内 x : 5
main内 i : 10
--------------------------------------
変数 i が実引数
変数 x が仮引数
コンストラクタによって
x = i ;
とされた。(見えないところでコッソリ)
それぞれの変数内の値は最初は同じだったが変数が置いてあるメモリのアドレスは全く別。値がコピーされただけなのだ。
なので x の値をいじっても i には一切影響しない。
これを踏まえて
--------------------------------------
void Test(int& x){ //「&」が付いた
cout << "Test関数内 x : " << x << endl;
x -= 5;
cout << "Test関数内 x : " << x << endl;
}
int main(){
int i = 10;
Test(i);
cout << "main内 i : " << i << endl;
}
(結果)
Test関数内 x : 10
Test関数内 x : 5
main内 i : 5
--------------------------------------
さっきの内容とほぼ一緒だが関数の仮引数の部分で & をつけた。
受け取り側で宣言時に & を付けると送り側を参照するようになる。
ただし参照先を変更することは出来ない。(参照先の値を変更出来ないんじゃなくて
別の変数を参照したくなっても乗り換えが出来ないと言う意味)
ちなみに
--------------------------------------
void Test(int& x){
printf("%p\n",&x);
cout << "Test関数内 x : " << x << endl;
x -= 5;
cout << "Test関数内 x : " << x << endl;
int&t = x; // i を参照している x をさらに t が参照してみる
printf("%p\n",&t);
cout << "Test関数内 t : " << t << endl;
t -= 2;
cout << "Test関数内 x : " << x << endl;
cout << "Test関数内 t : " << t << endl;
x -= 1;
cout << "Test関数内 x : " << x << endl;
cout << "Test関数内 t : " << t << endl;
}
int main(){
int i = 10;
printf("%p\n",&i);
Test(i);
cout << "main内 i : " << i << endl;
}
(結果)
0x7fff5fbff71c
0x7fff5fbff71c
Test関数内 x : 10
Test関数内 x : 5
0x7fff5fbff71c
Test関数内 t : 5
Test関数内 x : 3
Test関数内 t : 3
Test関数内 x : 2
Test関数内 t : 2
main内 i : 2
--------------------------------------
「参照の参照」をするとちゃんと動くし、 x は参照してるし参照されてる立場だけど
x -= 1
でちゃんと反映される。
「 i 」「 x 」「 t 」全てのアドレスは同じになっていた。
ポインタの場合は
--------------------------------------
void Test(int* x){
cout << "Test内 [int* x = p] -> [ポインタx = " << x << "]" << endl;
cout << "Test関数内 x : " << *x << endl;
*x -= 5;
cout << "Test関数内 x : " << *x << endl;
}
int main(){
int i = 10;
int* p = &i;
cout << "main内 [int* p = &i] -> [ポインタp = " << &i << "]" << endl;
cout << "main内 [Test(p)] -> [Test( " << p << " )]" << endl;
Test(p);
cout << "main内 *p : " << *p << endl;
cout << "main内 p : " << p << endl;
cout << "main内 i : " << i << endl;
}
(結果)
main内 [int* p = &i] -> [ポインタp = 0x7fff5fbff6dc]
main内 [Test(p)] -> [Test( 0x7fff5fbff6dc )]
Test内 [int* x = p] -> [ポインタx = 0x7fff5fbff6dc]
Test関数内 x : 10
Test関数内 x : 5
main内 *p : 5
main内 p : 0x7fff5fbff6dc
main内 i : 5
--------------------------------------
10で初期化した変数 i のアドレスをポインタ変数 p にコピーする。
(宣言時に * を付けるとポインタ変数になる)
ポインタ変数は、別の変数のアドレスを自分のアドレスとして使える。
そしてそのアドレスに格納されている値を変更できる。
ポインタ変数のアドレスをいじりたい時 -> 「変数」
ポインタ変数内の値をいじりたい時 -> 「*変数」
2モード切り替え可能なのがポインタ変数。
さらにコピーしたアドレスから別の変数のアドレスへの変更も自由。
あと別件で、普通の変数に & を付けるとその変数のアドレスが露出する。
だけども、
int main(){
int i = 10;
int& t = &i; //これはダメ
}
理由はまだ不勉強なので不明。誰か教えてください。
ポインタとconstについて
--------------------------------------
void Test(const int* x1,int* const x2){
int s = 30;
x1 = &s; // const 型* 変数 const->型 ポインタ変数にアドレス代入OK(ポインタ変数自体は変更可能と言うこと)
*x1 = s; // const 型* 変数 [error] const->型 ポインタ変数のアドレス先に値代入NG(constが効いている)
x2 = &s; // 型* const 変数 [error] const->変数 ポインタ変数にアドレス代入NG(constが効いている)
*x2 = s; // 型* const 変数 const->変数 ポインタ変数のアドレス先に値代入OK(アドレスの本来の持ち主には効かないと言うこと)
}
int main(){
int i1 = 10;
int i2 = 20;
const int* p1 = &i1;
int* const p2 = &i2;
Test(p1,p2);
}
--------------------------------------
constとは定数(英語だと「constant」)のこと。
constをつけられたものは変更不可になる。
ポインタに使う場合がややこしいので使ってみたところ、
const 型* 変数の場合はポインタ変数にはかかってないのでポインタ変数のアドレスを変更することは可能、代わりにポインタが持っているアドレスに格納されている値は変更不可。
型* const 変数の場合は逆で、ポインタ変数にかかっているのでポインタ変数のアドレスを変更できない、代わりにポインタが持っているアドレスに格納されている値は変更可能。この、値は変えられるけどアドレスは変えられないってのは & を使った参照にそっくりですね!
ちなみに両方変更不可にしたい場合は両方にかけると良い。
「const 型* const 変数」
配列の場合
--------------------------------------
void Test(int x[ ]){ /
printf("&x[0] -> %p\n",&x[0]);
printf("x -> %p\n",x);
for(int i = 0 ; i < 5 ; ++i){
cout << x[i] << " " << flush;
}
cout << endl;
}
int main(){
int i[ ] = {10,20,30,40,50};
printf("&i[0] -> %p\n",&i[0]);
printf("i -> %p\n",i);
Test(i);
}
(結果)
&i[0] -> 0x7fff5fbff710
i -> 0x7fff5fbff710
&x[0] -> 0x7fff5fbff710
x -> 0x7fff5fbff710
10 20 30 40 50
--------------------------------------
仮引数で配列を貰うことがわかるようにする必要がある。
void Test( int x [ ] )
配列の場合中身の値を全てコピーすると無駄なのでアドレスを渡すのが基本になっているらしい。
配列の先頭 [ 0 ] のアドレスは配列名のアドレスと一緒なので(結果)のように
&i[0] のアドレスと
i のアドレスが一緒だ。
そしてTest関数に渡された後のTest関数内の配列 x のアドレスを見ると i と全く一緒のアドレスだ。このことから、配列の場合コンストラクタで渡されるのは配列の先頭アドレス、さらに要素数も渡されているらしい。
ちなみに仮引数( int x[ ] )は( int *x )でも同じ。
アドレスを扱うから同じ。
とりあえずここまで