<ポインタの演算>
ポインタ変数の演算には、注意が必要です。
int
data[]={10, 20, 30, 40};
int *ip =
data; /*
int型ポインタipを宣言し、配列dataの先頭アドレスで初期化 */
ip++; /*
ipの値に1を足す?? */
printf("%d\n",
*ip);
ポインタ変数ipを配列dataの先頭アドレスで初期化した後、3行目でipをインクリメントしていますが、実際にはここでどのような演算がなされているのでしょうか?
ポインタがアドレスを格納するための変数であること考えれば、ip++はアドレスの値に1を加えていると思うかもしれません。しかし、実際には出力が"20"であることからも分かるとおり、演算の結果、ipはdataの2番目(data[1])のアドレスを指しています。つまり、ip++によって、ipが示すアドレスはint型のサイズ分増えていることになります。ip+1, ip+2という演算結果も同様です。また減算も同様です。
#include
<stdio.h>
int main(void)
{
int
data[3];
int
*ip = data; /*
int型ポインタを宣言し、配列dataの先頭アドレスで初期化 */
/*
dataの各要素のアドレスと、ip, ip+1,
ip+2の値を比較 */
printf("&data[0]:
%p, ip : %p\n", &data[0],
ip);
printf("&data[1]:
%p, ip+1: %p\n", &data[1], ip+1);
printf("&data[2]:
%p, ip+2: %p\n", &data[2], ip+2);
return
0;
}
(実行例)
&data[0]:
0xbffffa90, ip : 0xbffffa90
&data[1]:
0xbffffa94, ip+1: 0xbffffa94
&data[2]:
0xbffffa98, ip+2: 0xbffffa98
つまり、同じ「ポインタに1を足す」という演算でも、そのデータ型に応じて、例えばchar型なら1バイト、short型なら2バイト、long型なら4バイト、double型なら8バイト、というように、適切なバイト数分インクリメントしてくれているのです。
一見ややこしいようですが、そのおかげでプログラマは、配列要素のデータサイズを意識しなくても、「次の要素」とか「10個前の要素」とかいう具合に、ポインタを使って配列にアクセスできるわけです。
<ポインタの型変換>
ポインタ変数への代入は、それと同じ型の変数のアドレスであるのが基本です。下の例ではいずれもint型です。
int *ip1,
*ip2, *ip3; /* int型ポインタip1, ip2, ip3を宣言 */
int n,
data[10];
ip1 = &n; /*
ip1に変数nのアドレスを代入 */
ip2 = data; /*
ip2に配列dataの先頭アドレスを代入 */
ip3 = ip1; /*
ip3にip1の値(nのアドレス)を代入 */
ただし、場合によってはあえてポインタの型を明示的に変換(キャスト)して用いる時もあります。下の例は、short型配列の各要素(2バイト)の上位1バイトと下位1バイトを入れ替えるものです。
#include
<stdio.h>
#define SIZE 4
int main(void)
{
char
*bytes; /* char型(1バイト)のポインタ変数bytesを宣言 */
short
*sp; /*
short型(2バイト)のポインタ変数spを宣言 */
short
data[SIZE]={1000, 2000, 3000, 4000}; /*
short型の配列dataを宣言し、初期化 */
int
i;
char
tmp;
bytes=(char
*)data; /* short型配列の先頭アドレスをchar型のポインタに変換してbytesに代入 */
for
(i=0; i<SIZE; i++) {
tmp
= *bytes;
*bytes
= *(bytes+1); /* bytes+1では、char型変数1つ分(=1バイト)増える */
*(bytes+1)
= tmp;
bytes
+= 2; /*
bytesはchar変数2つ分(=2バイト)進む */
}
sp=data;
for
(i = 0; i<SIZE; i++) {
printf("%d\n",
*sp);
sp++; /*
spはshort型変数1つ分(=2バイト)進み、dataの次の要素を指す */
}
return
0;
}
前半では、short型整数の配列dataのアドレスを、char型のポインタbytesに代入していますが、データ型が異なるのでキャストする必要があります。char型のポインタをインクリメントすることで、2バイトの配列要素の個々のバイトにアクセスすることができます。
char型のポインタbytesに代入されるアドレスと、short型のポインタspに代入されるアドレスは、全く同じです。ただ、「それをどんなデータ型の変数のアドレスか」をコンパイラに明示的に指示することによって、そのポインタのインクリメントを行う時のバイト幅を変えることができるわけです。