int& なのでint型のアドレスを渡せる!...はずなのだがエラーが起きる
C++ なら int& num と書けますが、これはCなので、
引数をint* numに替えるとエラーは起きなくなる
int* はつまりアドレスの中身、よってint型を渡すことになるので値渡しになるのでは...?
この「*」は変数(num)がポインタ変数であることを示す型修飾子 です。よって int * num
は「仮引数 num は int 型データを指すポインタ変数である」という意味です。
そして仮引数 num に渡ってくる値はアドレスです(アドレスを値渡しする)。ちなみに、C++の参照渡しも、渡ってくる値はアドレスです。
一方、num = 30; の「 」は間接参照する間接演算子 です。同じ「*」でも文法的な意味が違い、働きも違います。
関数の引数は変数定義と同じです。
int * np = &n;
と変数を定義する時の「*」も型修飾子です。「np はポインタだよ」という意味で、決して間接参照するのではありません。
int * np = &n;
は int * np; np = &n;
と分解できます。ここで初期値(&n)が代入される場所は変数 np であり、*np = &n;
ではありません。
同じ*でも違う意味なのは難しい
型名(int, char 等)で始まる文は変数定義です。その「」はポインタ修飾子です。
型名で始まらないなら通常の実行文。その「 」は間接参照です。
printf("%p", &pn);
変数pnのアドレス表示..?
はい。ポインタ変数もメモリ上に配置されますので、ポインタ変数自身にもアドレスがあります。そのアドレスを表示します。
printf("%d", pn);
は何の値を指しているのでしょうか?
でも表示できてしまう。ただし全くデタラメな数値
ポインタ変数 pn の値(=変数 n のアドレス)を10進数表示します。"%p" は16進表示しますが、"%d" なので10進数表示します。けっしてデタラメな値ではありません。
今どきのCコンパイラは32bit用と64bit用があります。32bitであればポインタは32bit(4byte)の値、64bitであればポインタは64bit(8byte)の値です。質問者のコンパイラがどちらか不明ですが、下のプログラムを私の手元の32bit用GCCでコンパイルし、実行させました。32bitの方がビット数が少ない分、わかりやすいので(必要なら64bitの結果も出せるが)。
さらに、プログラム後半に ppn というポインタへのポインタ を加えました(ダブルポインタとも呼ぶ)。n をポイントする pn、pn をポイントする ppn、という関係です。ポインタへのポインタがあるのは、ポインタ変数自身にもアドレスがあるから、ですよね。
実行結果は次のようになりました。
# ./a.exe
0x62cc4c : &n
0x62cc4c : pn
5 : n
5 : *pn
0x62cc48 : &pn
6474828 : (int)pn
62CC4C : (int)pn
0x62cc44 : &ppn
0x62cc48 : ppn
0x62cc4c : *ppn
30 : **ppn
変数はメモリ上に配置されることを思い出してください。
メモリとは1バイト(8bit)毎にアドレスが振られた、長大な配列です。32bit の int 型変数は連続した4バイトに、64bitの double 型は連続した8バイトのメモリに、割当てられます。
同様に、ポインタ変数も4バイトもしくは8バイトのメモリに割当てられます。そして、ポインタ変数の値は他の変数のメモリアドレス です。
C言語はメモリを直接操作するプログラミング言語です。ポインタ変数はその象徴です。(n のような)単純な変数も、配列も、構造体も、ポインタも、リスト構造も・・・メモリ上に配置された変数の姿をイメージする事 。イメージできれば迷うことが少なくなります。
上の表示結果から n, pn, ppn のアドレスとデータ(値)を表にしてみました。
アドレス データ 変数名 0x62cc44 0x62cc48 ppn 0x62cc48 0x62cc4c pn 0x62cc4c 30 n
C
1 # include <stdio.h>
2 // C++ なら「int&」とも書けるが、Cでは int * num と書くしかない。
3 // 「int* はつまりアドレスの中身(=メモリの値)、
4 // よってint型を渡すことになるので値渡しになるのでは...?」<= 違う
5 // 呼び出す時点で int*num = &n; と同等のことが行われる
6 void ChangeValue ( int * num )
7 {
8 * num = 30 ;
9 }
10
11 int main ( void )
12 {
13 int n = 5 ;
14 int * pn = & n ;
15
16 printf ( "%p : &n\n" , & n ) ; // 変数nのアドレス表示する
17 printf ( "%p : pn\n" , pn ) ; // ポインタ変数pnの値を表示する
18 // 既にpnの値は n のアドレスである
19 // いろいろ表示してみるが
20 // printf("%d", *n); // 「*n」は不可。n はポインタ変数ではないから。
21 printf ( "%d : n\n" , n ) ; // 変数nの値表示
22 printf ( "%d : *pn\n" , * pn ) ; // 変数nの値を、ポインタ pn を使って表示する
23
24 // ポインタ変数もメモリ上に存在する。よってポインタ変数自身のアドレスがある
25 // 変数 pn のアドレスを表示する
26 printf ( "%p : &pn\n" , & pn ) ;
27
28 // 変数pnの値を10進数表示する。念のためキャストする(気休め?)
29 // 表示される値は決して「デタラメな数値」ではない。
30 printf ( "%d : (int)pn\n" , ( int ) pn ) ; // 10進表示
31 printf ( "%X : (int)pn\n" , ( int ) pn ) ; // 16進表示
32
33 ChangeValue ( & n ) ; // *num = 30; を実行させる
34
35 // ポインタへのポインタを使う
36 int * * ppn = & pn ;
37 printf ( "%p : &ppn\n" , & ppn ) ; // ポインタへのポインタ自身のアドレス
38 printf ( "%p : ppn\n" , ppn ) ; // ポインタへのポインタの値
39 printf ( "%p : *ppn\n" , * ppn ) ; // ポインタへのポインタが指す先
40 printf ( "%d : **ppn\n" , * * ppn ) ; // ポインタへのポインタが指す先の先
41 return 0 ;
42 }