#pragma twice

KAB-studio > プログラミング > #pragma twice > 061 Version 4.11 ポインタとキャスト

#pragma twice 061 Version 4.11 ポインタとキャスト

前のページへ 表紙・目次へ 次のページへ

 Version 4.11
ポインタとキャスト

前回したことをもう一度復習!
えっと、まず変数はメモリに置かれていて、その変数の場所を示すのがア
ドレスで、アドレスを入れることができる変数が、ポインタ!
大事なことは?
ポインタの中にはアドレスが入ってる! だよね
そういうこと
で、ポインタは、 * でアドレスが指す変数を見ることができたり、足し
算引き算で〈となり〉を見ることができたりする、だよね
そう、ポインタのサイズで移動する量が決まるから。その辺を踏まえつつ、
次に進みます。まずはこの関数

void MovePointerWithSquareBrackets()
{
    int i = 100;
    int *pi = &i;
    TRACE( "%X\n", pi );  // 64F530
    TRACE( "%X, %X, %X\n", pi[0], pi[1], pi[2] ); // ここに(略)。
}

ん? この前のに似てるね
前回の MovePointer() と最初3行は同じ。最後の4行目がちょっと違う
まーとりあえず試してみましょー。メモリは

0064F530  64 00 00 00 8C F5 64 00 32 33 40 00 2C F8 64 00 C0 01 52 00

ってなってて、4行目の出力はっと

64, 64F58C, 403332

あ、メモリの、4マスずつで区切ると……

64 00 00 00 
8C F5 64 00 
32 33 40 00 
2C F8 64 00 
C0 01 52 00

上みっつ、この結果と一緒だ!
ご名答! ってことはつまり?
つまり、 pi[0] が 0064F530 の値になって、 pi[1] がその次の 0064F534
に、 pi[2] が 0064F538 になってるんだよね
ってことは?
ってことは、 pi[0] が pi 、 pi[1] が pi + 1 、 pi[2] が pi + 2 っ
てこと?
惜しい! 正確には

pi[0] : *pi
pi[1] : *( pi + 1 )
pi[2] : *( pi + 2 )

そっか、 [] には * が付いてるのと同じ機能も付いてるんだ!
 [] はポインタを使って特定のアドレスの値を取り出すときに便利
わざわざ * とか付けなくていいんだもんね
あと、 [] を使うと特に〈となりの値〉とかって雰囲気出るでしょ
出る出る、 pi[2] とかって〈2番目の値〉って感じだもの。実際そうだ

その辺が直感的に分かれば結構大丈夫かな
大丈夫!
さて、ちょっと中途半端なところでまとめるけど、ポインタの特殊な点に
は次の3つがあります

1: * 演算子でアドレスの値にアクセスできる。
2: ポインタへの足し引きはポインタの型のサイズのぶん行われる。
3: [] 演算子で上ふたつの機能を同時に実行できる。

特に * と [] は普通の整数型、つまり int とかじゃ使えないことに注意
これが〈アドレスは整数だけどポインタに入れなきゃいけない〉わけなん

ポインタの難しい点のひとつは、これらの演算子が普通の整数値型と違う
点。この特殊な部分をまずしっかり憶えておいて
んでもそんな難しくないような……
なら安心。これからまた難しくなるから
げげ
ではこの関数を

void BadCast()
{
    int i = 100;
    short *psh = &i;    // エラー。
}

ん!? int のアドレスを short のポインタなんかに入れられるの? 
あ、エラーが出る例なんだね
そう、こんなエラーが出ます

error C2440: 'initializing' : 
 'int *' から 'short *' に変換することはできません。
 指示された型は関連がありません; 変換には reinterpret_cast、 
 C スタイル キャストまたは関数スタイルのキャストが必要です。

こういう〈無理矢理なこと〉をする場合には〈キャスト〉をする必要があ
ります
キャスト?
ある型を、他の型であるかのように無理矢理見せかけることを〈キャスト〉
っていいます。キャストはこんなふうにします

void CStyleCast()
{
    int i = 100;
    short *psh = (short *)&i;
    TRACE( "%X, %X\n", &i, psh ); // 64F530, 64F530
}

あ、 (short *) っていうのが増えたね、これがキャスト?
そう。 &i は int * 型。それを無理矢理 short * 型に見せかけるのが
(short *) 。〈変えたい型を小カッコで囲む〉ってだけね
結果見ると、型が変わっただけでアドレス自体は変わんないんだね
そう、ポインタはポインタだからね。 int * と short * は同じポインタ
だけど、アドレスのある変数の型が違う、ってだけだから
ポインタはポインタ、アドレスはアドレス、だね
でもこれ使っちゃダメ!
ええっ!? じゃーなんで教えるのよ
古い方法では一般的だから、とりあえず使えないといけないから
この前の #define みたいなのってこと?
そういうこと。今は、こっちの方法を使います

void reinterpret_cast_Cast()
{
    int i = 100;
    short *psh = reinterpret_cast< short * >( &i );
    TRACE( "%X, %X\n", &i, psh ); // 64F530, 64F530
}

あれ? この reinterpret_cast ってどっかで見たような……
さっきのエラー表示に出てたね
ううん、そうじゃなくって……あ! Ver 3.5 ( No.30 )で見た!
げ! そ、そういえば……
あのときは……

    hWnd = reinterpret_cast< HWND >( 0x00000758 );

そうそう、ハンドルの 0x00000758 って数字を、 HWND 型の変数に入れる
ときに使ったんだ。ん? ってことはハンドルって……アドレス?
ぎくっ。実はそうなんだけど、それはまた今度
……なんか教える順番ちがくない?
ぎくぎくっ、まー今度ちゃんと教えるから
はいはい。こんときにこんなふうに教えてもらってるけど

reinterpret_cast< 変換先の型 >( 変換する値 )

今度のもそう?
そう、同じ。 &i は int * 型。それを無理矢理 short * 型に見せかける
のが reinterpret_cast< short * >( &i ) 。書式は上のような感じ
この方法が、さっきの小カッコのよりいい理由って?
見つけやすいってこと
見つけやすい?
小カッコの方は、関数とか演算とかでも使うからキャストしてる部分を検
索しにくいんだけど
 reinterpret_cast はキャスト専用のだから見つけやすい?
そう。こういうキャストって、無理矢理なことが多いから、問題を発生さ
せやすいんだよね。だから見つけやすくしておかないといけないから
それは確かに
それに、小カッコのはいろんな型にキャストできるけど reinterpret_cast
はポインタがらみでしかキャストできないから
いろんな型?
そう、これからいろんなキャストをしていく必要があるからね。そういう
場合には〈ポインタ関係のキャスト〉しかできない reinterpret_cast の方
が安全だから
また出た、安全志向
これはうるさく言ってくからねー。実際、ポインタ関係は問題出やすいか

そなの?
その辺も絡めて、この short のポインタについて見てみようか

void CastAndShow()
{
    int i = 0x89ABCDEF;
    short *psh = reinterpret_cast< short * >( &i );
    TRACE( "%X, %X\n", psh[0], psh[1] );
}

1行目が 0x89ABCDEF になってる。で、2行目はおんなじで、3行目はそ
の short のポインタを使って値を見てるんだよね
 psh[0] と psh[1] のアドレスは?
 psh[0] は &i と同じでしょ。 psh[1] は……あれ? 4バイト増えるの
かな、2バイト増えるのかな
2バイトです。で、結果はこうなります

FFFFCDEF, FFFF89AB

??
ん、 TRACE() がうまく表示してくれてないけど、実際にはこういうこと
だね

CDEF, 89AB

これって、もしかして i の最初2バイトとあと2バイト?
そういうこと。つまり、 short のポインタを使っているときには、 short
として扱われるってこと
どゆこと?
つまり2バイトサイズで判断されるってこと。アドレスの足し引きも2バ
イトだし、 short ひとつが占める領域も2バイト
つまり、元の int 型っていうのは無視されちゃってるんだ
そう、そこが大事! メモリって、実際には全然区切られてないってこと。
ただ単にデータが置かれてるだけ
もしかして、その区切りを決めるのが、型ってこと?
そういうこと。型があって初めて、メモリのその領域が〈何を意味するの
か〉が決定されるわけ。たとえばさっきの結果

CDEF, 89AB

これ、左右継なげても元の int にはならないでしょ
ホントだ。バイトオーダーのせい?
そう。メモリには EF CD AB 89 って並んでるでしょ。これは int の並び。
short は2マスずつ取って、その中で左右入れ替えるから CDEF と 89AB って
なるわけ
メモリのデータを見るときに、 int で見るとの short で見るのとの違い
が出るわけだ。こうも違うと、確かにキャストって怖いねー
でもキャストやポインタがあるからこそ、こういうメモリに直に触れるよ
うな操作もできるんだよね
そういう考え方もあるか。こーゆーのも〈みなし〉ってこと?
そういうことだね

/*
    Preview Next Story!
*/
〈みなし〉はこれからもいっぱい出てくるから、覚悟するように
へいへい
う”、なんか態度悪くない?
水希ちゃんがそうみなしてるだけよ
そ、そうなのか?
唐辛子が辛いのもお化けが怖いのも、所詮、そう見なしてるだけなのよ
というわけで次回
< Version 4.12 const 型のポインタ >
につづく!
じゃあ火美ちゃんの髪の毛が変なのも?
と、当然そう見なしてるだけよ
性格がひねくれてるのもガラ悪いのも?
がーっムカツク!!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。