ポインタ変数はメモリアドレスを格納するための特殊な変数です。
使用する際にはいくつかの注意点がありますので項目別に説明します。
sizeof命令を使うことで、変数や配列、構造体のサイズ(バイト数)を調べることが出来ました。
私の使っている環境では、char型は1バイト、int型は4バイトという結果でした。
では、ポインタ変数はどうでしょう?
次のプログラムを作り、実行してみてください。
<sample program col032-01>
#include <stdio.h> int main(void) { int *p; printf("p size = %d\n", sizeof(p)); return 0; } |
<実行結果>
p size = 4 続行するには何かキーを押してください・・・
私の環境ではint型のポインタ変数のサイズは4バイトでした。
では、char型のポインタ変数ではどうでしょう。
<sample program col032-02>
#include <stdio.h> int main(void) { char *p; printf("p size = %d\n", sizeof(p)); return 0; } |
<実行結果>
p size = 4 続行するには何かキーを押してください・・・
char型ポインタ変数も4バイトです。
ポインタ変数はメモリアドレスを入れる変数です。
char型だからといって1バイトのサイズしかなければ「0〜255」番地までしか指せないということになってしまいます。
ポインタ変数のサイズは型によって変わりません。
ポインタ変数に対して加算や減算を行うことがあります。
その際にどのような動きをするのか見てみたいと思います。
まずは↓のプログラムを作ってください。
<sample program col032-03>
#include <stdio.h> int main(void) { int a; int *p; p = &a; printf("p = %p\n", p); return 0; } |
<実行結果>
p = 009FF7B4 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
変数aのアドレスをポインタ変数pに入れて表示するプログラムです。
このポインタ変数に1を加えて表示してみます。
<sample program col032-04>
#include <stdio.h> int main(void) { int a; int *p; p = &a; printf("p = %p\n", p); p++; printf("p = %p\n", p); return 0; } |
<実行結果>
p = 007DFCF8 p = 007DFCFC 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
「007DFCF8」というアドレスに1を加えると「007DFCFC」となりました。
16進数で表示されていますので、「8」から「C」に変わったということは4増えたということです。
他の型のポインタ変数ではどうでしょうか。
<sample program col032-05>
#include <stdio.h> int main(void) { char a; char *p; p = &a; printf("p = %p\n", p); p++; printf("p = %p\n", p); return 0; } |
<実行結果>
p = 00B7F783 p = 00B7F784 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
char型ポインタでは1だけ増えました。
ついでにshort型(16ビット)でも試してみましょう。
<sample program col032-06>
#include <stdio.h> int main(void) { short a; short *p; p = &a; printf("p = %p\n", p); p++; printf("p = %p\n", p); return 0; } |
<実行結果>
p = 006CFEF4 p = 006CFEF6 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
2増えました。
ポインタ変数に1加えた際に増加する値は、それぞれの型のサイズに当てはまります。
int型ポインタ変数では、int型変数のサイズである4
char型ポインタ変数では、char型変数のサイズである1
short型ポインタ変数では、short型変数のサイズである2
なぜこのような仕組みになっているのでしょうか。
配列のことを考えてみます。
「メモリアドレス」で配列名が配列の先頭アドレスを表すことを説明しました。
また、「ポインタ変数とアドレス渡し1」でポインタ変数の前に「*」を付けることで、ポインタ変数に入っているアドレスの中身を表すことも説明しました。
それを踏まえて次のプログラムを動かしてみましょう。
<sample program col032-07>
#include <stdio.h> #define DATA 3 int main(void) { int data[DATA] = { 12, 25, 37 }; int *p = data; printf("*p = %d\n", *p); return 0; } |
<実行結果>
*p = 12 続行するには何かキーを押してください・・・
配列dataの先頭アドレスをポインタ変数pに入れました。
「*p」と書くことでポインタ変数に入っているアドレスの中身、という意味になるので、配列の先頭に入っている「12」が表示されたのです。
このポインタ変数pに1を加えるとどうなるでしょう。
int型ポインタ変数ですから4増えますね。
ということは、配列dataの先頭アドレス+4番地を示すことになります。
先頭から4番地先には何があるのでしょうか、メモリの中身を書いてみます。
int型変数は1つ4バイトです、メモリは1バイトごとに番地が割り振られています。
配列dataがどのようにメモリに配置されているか見てみましょう。
<sample program col032-08>
#include <stdio.h> #define DATA 3 int main(void) { int data[DATA] = { 12, 25, 37 }; int i; for (i = 0; i < DATA; i++) { printf("&data[%d] = %p\n", i, &data[i]); } return 0; } |
<実行結果>
&data[0] = 0021FEB0 &data[1] = 0021FEB4 &data[2] = 0021FEB8 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
先頭のアドレスに4加えた場所に次のデータがあることが分かります。
これを見た上で次のプログラムを動かし、仕組みを理解してください。
<sample program col032-09>
#include <stdio.h> #define DATA 3 int main(void) { int data[DATA] = { 12, 25, 37 }; int *p = data; p++; printf("*p = %d\n", *p); return 0; } |
<実行結果>
*p = 25 続行するには何かキーを押してください・・・
ポインタ変数pに1を加えることで、配列中の次のデータを指すようになりました。
このように、配列の「添え字」を使わず、アドレスを使ったアクセス方法もあるのです。
↓のような方法でもアクセス出来ます。
<sample program col032-10>
#include <stdio.h> #define DATA 3 int main(void) { int data[DATA] = { 12, 25, 37 }; int *p = data; printf("*(p + 1) = %d\n", *(p + 1)); return 0; } |
<実行結果>
*p = 25 続行するには何かキーを押してください・・・
計算式に括弧が付いているのは演算子の優先順位の関係です。
*p + 1 |
と
*(p + 1) |
では、意味が異なります。
「+」演算子よりも「*」演算子の方が優先度が高いため、括弧が無ければ先に計算されます。
試しに括弧を外して動かしてみてください。
<sample program col032-11>
#include <stdio.h> #define DATA 3 int main(void) { int data[DATA] = { 12, 25, 37 }; int *p = data; printf("*p + 1 = %d\n", *p + 1); return 0; } |
<実行結果>
*p = 13 続行するには何かキーを押してください・・・
先に「*p」が計算されます。
「*p」はポインタ変数pに入っているアドレスの中身ですから「12」のことです。
その「12」に1を加えますので、「13」と表示されています。
括弧が付いていると「p+1」が先に計算されますので、pに入っているアドレスに1を加えます。
※実際の値はint型ポインタなので4増えています。
計算後のアドレスの中身を表示していますので「25」と表示されるのです。
同じ型のポインタ同士であれば代入は問題なく行えます。
<sample program col032-12>
#include <stdio.h> int main(void) { int a; int *p = &a; int *q = p; printf("&a = %p\n", &a); printf("q = %p\n", q); return 0; } |
<実行結果>
&a = 00CAFD9C q = 00CAFD9C 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
ポインタ変数pもポインタ変数qも同じint型ポインタ変数なので、何も問題ありません。
では、型が異なるポインタ変数の代入を試してみましょう。
<sample program col032-13>
#include <stdio.h> int main(void) { int a; int *p = &a; char *q = p; printf("&a = %p\n", &a); printf("q = %p\n", q); return 0; } |
コンパイルの時点で、
error C2440: '初期化中': 'int *' から 'char *' に変換できません。
というエラーが発生し実行出来ませんでした。
ある型のアドレスを別の型のポインタに入れることは非常に危険なためエラー扱いになっています。
基本的にやってはならないことだと思ってください。
しかし、私の環境では、エラーの下に何かメッセージが書いてあります。
note: 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
「キャスト」とは型変換だとコラムに書きました。
実はポインタ変数も型変換が出来るのです。
上記のメッセージを読んで、「キャストすれば出来るんだ!」と考える人がいます。
少し試してみましょう。
<sample program col032-14>
#include <stdio.h> int main(void) { int a; int *p = &a; char *q = (char*)p; printf("&a = %p\n", &a); printf("q = %p\n", q); return 0; } |
<実行結果>
&a = 005EFBF4 q = 005EFBF4 続行するには何かキーを押してください・・・
※実行結果は毎回変わる可能性があります。
本当はint型のポインタなのですが、char型のポインタ変数としてキャストすることでエラーが消えました。
実行すると変数aのアドレスが入っていることが分かります。
上手くいっているように見えますが、これは非常に危険なことです。
変数aに初期値を入れて、アドレス経由で表示してみましょう。
<sample program col032-15>
#include <stdio.h> int main(void) { int a = 12; int *p = &a; char *q = (char*)p; printf("*p = %d\n", *p); printf("*q = %d\n", *q); return 0; } |
<実行結果>
*p = 12 *q = 12 続行するには何かキーを押してください・・・
どちらも正しい結果を表示しているように見えます。
では変数aの値を変えてみます。
<sample program col032-16>
#include <stdio.h> int main(void) { int a = 10000; int *p = &a; char *q = (char*)p; printf("*p = %d\n", *p); printf("*q = %d\n", *q); return 0; } |
<実行結果>
*p = 10000 *q = 16 続行するには何かキーを押してください・・・
int型ポインタ変数pの方は正しい値を表示しましたが、char型ポインタ変数qは正しい値を表示出来ていません。
基本的にchar型ですから10000という大きな数値は扱えません。
細かい説明をしてみます。
10進数の10000は、16進数32ビット(4バイト)で「00002710」です。
変数aにはこの数値がバイトごとに格納されています。
変数a メモリ +--------+ 00A2FB64番地 | 10 | +--------+ 00A2FB65番地 | 27 | +--------+ 00A2FB66番地 | 00 | +--------+ 00A2FB67番地 | 00 | +--------+
ポインタ変数pはint型ポインタ変数なので、メモリの4バイト分を見て「10000」だと判断します。
ポインタ変数qはchar型ポインタ変数なので、メモリの1バイト分しか見ず「16」だと判断しています。
※00A2FB64番地には、16進数で「10」が入っていますので、10進数で「16」になります。
このような現象は環境によってどうなるか分からないため、これを利用したプログラムなどは絶対に組んではなりません。
ブラウザの戻るボタンで戻ってください。