#pragma twice

KAB-studio > プログラミング > #pragma twice > 062 Version 4.12 const 型のポインタ

#pragma twice 062 Version 4.12 const 型のポインタ

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

 Version 4.12
const 型のポインタ

ポインタ編もいよいよ大詰め!
いつの間にポインタ編?
前回はキャストについて見てみたけど
型は、どうメモリをみなすか、ってゆーものってことだよね
そうそう。で、今日はそれはおいといて、別の話
え”、ってことはまた新しいこと憶えるの?
そういうこと。まずはこの関数から

void Sub2Pointer()
{
    int i = 0;
    int *pi = &i;
    *pi = 100;      // ここに注意。
    TRACE( "%d, %d\n", i, *pi );
    // 100, 100
}

3行目の *pi に注意、ねぇ
結果どうなってる?
両方とも 100 。……? 両方ともって、 i の方も?
ポインタに * 付けると?
ポインタが指し示す変数に……ってことは、 *pi = 100 って、 i = 100
と同じこと!?
そういうこと。ポインタに * を付けると、ホントに、元の変数と同じよ
うに振る舞うから
値取れるだけじゃないんだー
実感掴めないなら、メモリで直接見た方がいいかな
その方がいいかも。〈注意〉の行にブレークポイント付けて止めてっと

0064F530  00 00 00 00 8C F5 64 00 E2 30 40 00 2C F8 64 00 C0 01 52 00

うん、 i には 0 入ってるから4マス分ゼロだね。で、ひとつ進めると

0064F530  64 00 00 00 8C F5 64 00 E2 30 40 00 2C F8 64 00 C0 01 52 00

を”、ホントだ……
つまり、 *pi と i はまったく同じ物ってこと
ほーっ。分身みたいなものってことね
それはちょっと違うかな。分身、鏡像、コピー、クローン。これってオリ
ジナルと独立してるでしょ
どゆこと?
分身を攻撃したら?
分身はかき消えて、オリジナルが〈どこを見ている!〉って……あ、 *pi 
は i に影響するから!
そう、だからポインタは分身じゃないわけ。例えるなら
スタンド!
……例えるなら、銀行と ATM 機みたいなものだねっ!
無視された……まー確かに ATM 使うと預け入れたりおろせたりできるも
んねー
この場合、銀行が元変数 i で、 ATM がポインタ pi 
銀行のカードは pi の中のアドレスってとこかな?
そんな感じだね。さてさて、実はこれはとっても危険なことなんです
とっても?
もし i のポインタがいーっぱい作られたら、いつ i が書き換えられちゃ
うかビクビクものでしょ
それっていろんな人があたしの口座のカード持ってるようなもんでしょ、
それは怖い!
それに、こんなこともできちゃいます

void BadPointer()
{
    int i = 0;
    int *pi = &i;
    pi[1] = 100;        // ここにブレークポイント。
    TRACE( "%d, %d\n", i, *pi );  // 0, 0
}

あ、 pi[1] ってことは…… pi[0] が *pi で pi[1] が *( pi + 1 ) だ
から、隣に数字入れてるんだね。……隣ってなんかの変数だっけ
ううん、何に使われてるか分からないメモリ上の領域
そ、それってまずくない!?
もちろんまずいよ。いつも通り試してみて
ブレークポイント付けて、実行して、メモリ開いて

0064F530  00 00 00 00 8C F5 64 00 C2 1B 40 00 2C F8 64 00 C0 01 52 00

で、1行進めて……

0064F530  00 00 00 00 64 00 00 00 C2 1B 40 00 2C F8 64 00 C0 01 52 00

ああっ、右隣が 100 に書き換えられちゃった!
これはもちろんまずいこと。何か大事なことに使ってる領域かもしれない
からね
隣の人の机に物置いちゃうようなもんよね……
もう1行進めると?
 i の中身が出るけど、やっぱ 0 のままね
じゃ、普通に進めて
? 〈実行〉ボタンを押してっと……あ、なんか変なダイアログ出た!

Microsoft Visual C++ Debug Library

Debug Error!

Program:
(略)Test.exe
Module:
File: i386\chkesp.c
Line: 42

う”、やっぱさっきのは悪いことだったみたい……
【中止】ボタン押せば終了するから
うん閉じた。あー、なんかこーゆーのって心臓に悪いなー
壊しちゃってるみたいだからね。でも、こうやって操作できるメモリは、
そのアプリ専用のだから、 OS とか他のアプリとかには影響ないから
あ、そういうもんなんだね。ちょっと安心
で、ようするに〈ポインタを通して書き込める〉っていうのは危険ってこ
と。それは分かった?
うん、かなり分かった。で、それを防ぐ方法がある?
あるんです。それは const を使うこと
 const って Ver 4.09 ( No.059 )で出てきたのだよね
そう、値を変えないようにするの。でも、ポインタで使う const はちょっ
と違うんだよね

void ConstValuePointer()
{
    int i = 0;
    const int *pi = &i;
    *pi = 100;      // エラー。
}

??
まず1行目は int 型変数 i を作ってます
それは分かるよー。問題は2行目。えっと、さっきのと比べると……

          int *pi = &i;
    const int *pi = &i;

あ、 const が付いてるか付いてないかってだけだね
そこがミソ。これは

      int 型変数のアドレスを入れるためのポインタ
const int 型変数のアドレスを入れるためのポインタ

って考えればいいかな
え、だって実際に入れてるの、 const int じゃないじゃん
そう、そこが大事。つまり const 型のポインタには、 const 型のアドレ
スだけじゃなくて、非 const 型のアドレスも入れられるってこと
そういう仕組みになってるってこと?
ポインタを〈読み取り専用〉にしたいからこういう仕組みがある、って考
えた方がいいかもね
で、3行目。ビルドするとこの行がエラーになるんだね

error C2166: 左辺値は const オブジェクトに指定されています。

ってことは、 *pi が const int って見なされるんだ
元の変数は int だけど、ポインタは const int だから、このポインタの
方で見なされるわけ
つまり、読み取り専用ポインタを作る時には、 const 型へのポインタを
作ればいいってことねー
そうそう、そう簡単に考えた方がいいかも
……ふと思ったんだけど、逆ってどう?  const int を int * に入れた
りとか
やってみましょう
あ、そうだね。こんな感じ?

void BadConstPointer()
{
    const int i = 0;
    int *pi = &i;   // エラー。
}

あ、エラー

error C2440: 'initializing' : 
    'const int *' から 'int *' に変換することはできません。
    変換で修飾子が失われます。

へー、これはできないんだ
もしこれができちゃうと、 const なはずの i の値が変えられちゃうで
しょ
だから、その前の、ポインタの段階でできないようになってるってことな
んだ。でもさ、それはそれで変くない? 直接は関係ないんだから、できて
もいいと思うけど
そこが大事。 const って、単に定数を作るためのものっぽいでしょ
うん、ただの機能って感じ
でも、実際には〈定数としての機能を守る〉っていうことが前提になって
いて、そこから const なんかのルールが決められている、って考えた方が
いいかも
つまり、まず最初に〈定数を変えない仕組み〉ありき、 const を const
ポインタに入れられないルールがそこから付けられたってこと?
実は C++ にはそういう部分がすごく多いんだよね。一見〈色々使えるた
だの機能〉のように見えて、実は〈ある目的のための機能〉ってことが
その辺知らないと難しくない?
だから教えてるんでしょう
あ……
こういうことは、本読んでも分からない部分かもね
まとめると

      int のアドレスを       int * へ
      int のアドレスを const int * へ
const int のアドレスを const int * へ

は良くて

const int のアドレスを       int * へ

はダメ
つまり const を非 const にするのはダメってこと。書き換えられちゃう
からね
それはつまり、書き換えられないようにそういうルールになってる、だよ

そういうこと
逆に非 const を const はOKね。読み取り専用にしたいから、こういう
ことができるようになってる
それはつまり、火美ちゃんが〈読み取り専用ポインタにしたい〉って思っ
たら const 型へのポインタにすればいいってことだからね
あ、プログラムを書くとき〈どうすればいいのかな?〉って思ったら、そ
う考えていけばいいんだ
そういうこと。言語仕様っていうのは、ちゃんと目的があるってことだね

/*
    Preview Next Story!
*/
また新しい概念出てきたねー
〈みなし〉の次に今回の〈目的が先、ルールが後〉、かな
なんかさー、宗教っぽくない?
実際、ものの見方変わっちゃうかもね
というわけで次回
< Version 4.13 寿命とポインタ >
につづく!
見方変わるとどうなる?
常識が亡くなる
う”っ……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。