C言語のアドレス演算子の不思議

プログラミング
スポンサーリンク

はじめに

前々から、C言語のポインタで不思議だったこと。

単項&演算子をアドレス演算子といい,&の後に続くオペランドのポインタ(アドレス)を表します。
(3) 記述例

int a , *p ;
p = &a ;

解説
&aaのアドレスを表し,ポインタpaのアドレスを代入する式です。

最適化C 言語 より
http://www.imit.chiba-u.ac.jp/new-system/sr/man_hitachi_c/MAN/3000/03C3100D/C310057.HTM

上記の解説を見て、アドレス演算子をつければ、その変数のアドレスが分かる。アドレスは数字だと、そのまま理解すると少し不思議なことが起きます。

ポインタ型のアドレスの不思議

アドレスは数値型?

例えば、以下のような計算をしたとします。結果はどうなると思いますか。

#include <stdio.h>
main() {
	int target;
	int x = &target + 1;
	int y = &target;y += 1;
	printf("x = %d\ny = %d\n",x,y);
}

実は、xとyが同じ値になりません!

この理由についてC言語が得意な友人に色々教えていただきました。その結果、「数値とアドレスは違うもの。&をつけるとポインタ型になる。」ということが分かりました。どうやらポインタ型というものは、数値のようだがアドレスとしての計算を行うので、ポインタ型に加算や減算を行うと、通常の数値型としての計算にあてはまらないようなのです。

ポインタ型への加減算について

以下のようにポインタ型+通常の数値の加算は可能です。

int x = &target + n;

この計算式は、内部的には以下のように自動でsizeof(X)を数値に掛け算した値と同じになります。ポインタ型での加減算は注意しないとおかしな計算をしていることになるので注意が必要です。

int x = (int)&target + sizeof(target) * n;

ポインタ型での禁則事項

ポインタ型同士の加算を行うとエラーが発生します。恐らくアドレス同士で加算すること自体が通常行わない演算のためだと思われます。

&target + &target ←エラー

他に注意する点としては、ポインタ型同士の加算の他に、ポインタ型に数値の掛算・割算はエラーもおきます。

#include <stdio.h>
main() {
	int* target;
	target = (int *)1 + (int *)2;
	target = (int *)1 * 1;
	target = (int *)1 / 1;
}

まとめ

教科書や解説サイトとかで、ポインタ型のアドレスを%pではなく整数型を表示させる%dで表示するように紹介することが多いため、「アドレスと整数は同じなんだ。アドレスって数値だし納得!」みたいな錯覚すると上記のような混乱を起こす可能性があります。

ポインタに関して、苦しんで覚えるC言語アドレスを記憶する変数 がつっこんだところまで解説していて分かりやすいです。

オマケ

話は全く変わるのですが、友人が書いたサンプルが以下にあります。

#include <stdio.h>
main() {
	int x = 1,y[] = {0,100};
	printf("%d\n",x[y]);
}

この書き方には驚きました。y[2] が *(y+2) に対応しているから、こういう書き方できるそうです。C言語は奥が深いです。いろいろ教えてくださって、ありがとうございます。m(_ _)m

これらのことが分かると、次のようなネタコードが書けますので紹介します。

ビットシフト無しで1バイトずつ取り出す。(1回の加算と1回のビット論理和)

#include <stdio.h>
#define GET(T,N) ((int)*((char*)(&T)+(int)(N)))
main() {
	int i,target = 0×11223344;
	for(i=0;i
(すみません。ブログの仕様変更で消えてしまった!)

加算のみで5の倍数を表示する(3回の加算)

#include <stdio.h>
#define GET(T,N) (int)((T*)0+(int)(N))
main() {
	int X = 8;
	printf("%d\n",GET(long,X)+GET(char,X));
}

コメント

タイトルとURLをコピーしました