C言語の変数は「値」と「住所」を持つ
まず、大前提として、C言語のすべての変数はメモリ上に存在します。そして、それぞれの変数には2つの重要な情報があります。
- 値(中身): 変数に格納されているデータそのもの。
- アドレス(住所): その変数がメモリ上のどこにあるかを示す一意の場所。
例えば、int a = 100;
というコードを実行すると、メモリ上には「値100
」が格納され、その場所には「a
という名前」と「一意のアドレス」が割り当てられます。
「普通の変数」と「ポインタ変数」の違い
では、普通の変数とポインタ変数にはどのような違いがあるのでしょうか。それは、何を変数の中身として格納するかという点です。
変数の種類 | 中身として格納するもの |
---|---|
普通の変数(a ) |
整数や文字などのデータ(値) |
ポインタ変数(p ) |
他の変数のアドレス(住所) |
&と*の演算子
ポインタを扱う上で混乱しやすいのが、&
と*
の演算子です。これらを整理して理解するために、それぞれの要素を「普通の変数」と比較しながら見ていきましょう。
変数a
→ 変化させられるものはa
の1つです。
要素 | 構文 | 意味 | 変化可能か |
---|---|---|---|
値 | a |
変数a に格納された値 |
できる(a = 300; ) |
アドレス | &a |
変数a の住所 |
できない(メモリ上の場所は固定) |
ポインタ変数p
→ 変化させられるものは*p
とp
の2つです。
要素 | 構文 | 意味 | 変化可能か |
---|---|---|---|
値 | *p |
p が指し示す住所にある値 |
できる(*p = 300; ) |
アドレス | p |
ポインタ変数p に格納された住所 |
できる(p = &b; ) |
ポインタ変数自身のアドレス | &p |
ポインタ変数p 自身の住所 |
できない(メモリ上の場所は固定) |
具体的なコードで動きを追う
#include <stdio.h>
void show(int,int,int);
int main(void){
int a = 100;
int b = 200;
int *p = NULL;
p = &a;
show(a, b, *p);
*p = 300;
show(a, b, *p);
p = &b;
show(a, b, *p);
*p = 400;
show(a, b, *p);
return 0;
}
void show(int n1,int n2,int n3){
printf("a = %d b = %d *p = %d\n",n1,n2,n3);
}
出力結果
a = 100 b = 200 *p = 100
a = 300 b = 200 *p = 300
a = 300 b = 200 *p = 200
a = 300 b = 400 *p = 400
p = &a;
: p
の中身がa
のアドレスに上書きされます。a
とb
の値は変わりません。
*p = 300;
: p
が指している先(a
)の値が300に上書きされます。この時、p
の中身(&a
)は変わりません。
p = &b;
: p
の中身がb
のアドレスに上書きされます。これでp
はb
を指すようになります。a
とb
の値は変わりません。
*p = 400;
: p
が指している先(b
)の値が400に上書きされます。この時、p
の中身(&b
)は変わりません。
配列とポインタ
C言語では、「配列名はポインタとして扱われる」というルールがあります。これにより、配列とポインタをほとんど同じように扱うことができます。
a[i]
と*(a + i)
は同じ!
配列(int a[10]; ) |
ポインタ(int *p; ) |
|
---|---|---|
役割 | データを連続して格納する箱 | アドレスを格納する変数 |
サイズ | 固定(例:sizeof(a) は40 バイト) |
固定(通常8 バイト) |
代入 | 負荷(a = ... はエラー) |
可能(p = &a[0] など) |
宣言時 | メモリが確保される | アドレスを格納する箱だけ確保される |
a
: 配列の先頭要素a[0]
のアドレス
a + 1
: 配列の2番目の要素a[1]
のアドレス
*a
: 配列の先頭要素a[0]
の値
*(a + 1)
: 配列の2番目の要素a[1]
の値
&a
: 配列全体の先頭アドレス
&a + 1
: 次の配列の先頭アドレス(※配列の2番目の要素a[1]
のアドレスではない)
難しい...