#pragma twice

KAB-studio > プログラミング > #pragma twice > 113 Version 6.13 手抜きをするな!

#pragma twice 113 Version 6.13 手抜きをするな!

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

 Version 6.13
手抜きをするな!

演算子関係のバグ、今日は優先順位について
 + が先に計算されて、そのあと = ってヤツね。これ教えてもらった時、
優先順位は憶えちゃダメって言ってたよね
うん。たとえばこれ

void CDebugDlg::OnButton1() 
{
    int iAry[2];
    iAry[0] = 100;
    iAry[1] = 200;
    int *pi = iAry;

    *pi++;  // ここに注目!
    TRACE( "%d\n", *pi );
    // 200
}

ふたつ入った配列用意して、そのポインタを pi に入れてるんだよね
で、 *pi++ 。これ、 ++ と * 、どっちの優先順位が高いかで結果が違う
でしょ
 ++ の方が高いと *( pi++ ) と同じで、それだと iAry[1] と同じだから
 200 かな。 * の方が高いと ( *pi )++ 、 ( iAry[0] )++ だから 101 
結果は 200 、つまり ++ が先に処理されてそのあと * が実行されます
 *( pi++ ) なわけね。MSDN を見てもそうだね
で、ここで重要なのは、優先順位が * よりも ++ よりも高い、ってこと
を憶えちゃダメ! ってこと
そこでそうなるわけね。でもなんで?
憶え間違うかもしれないから
憶え間違いって……
逆に憶えちゃうかもしんないし、思い出す時に勘違いするかもしんない

まぁそうかもしんないけど
だから、積極的に () を使おう! って話

void CDebugDlg::OnButton1() 
{
    int iAry[2];
    iAry[0] = 100;
    iAry[1] = 200;
    int *pi = iAry;

    ( *pi )++;  // ( iAry[0] )++ を望む場合。
    TRACE( "%d\n", *pi );
    // 101
}

ってね。これなら確実でしょ
うん、確実
つまりね、 C++ の細かい仕様なんか利用しないで、見た目に分かりやす
いものを利用しましょうって話
ようするに、自分を信用するな、ってことでしょ?
極論すればね。でもこれはプログラミング界一般に言われてること
うん分かってるよ。 const とかそーじゃん
そうそう。確かに () 使うのって結構手間掛かるけど
 () が増えると見にくいよね……
ってゆーか、基本的には1行に詰め込むことが間違いかな。この例だと仕
方ないけど、たとえば

void CDebugDlg::OnButton1() 
{
    int i1 = 100, i2 = 200;
    TRACE( "%d, %d\n", i1, i2 );
    // 100, 200
}

前々回のだね。これは , でいっぺんに2個変数を作ってるのがまずいん
でしょ
そう。他にも ?: とか、あとこういうのも

void CDebugDlg::OnButton1() 
{
    int i = 100;
    TRACE( "%d, %d\n", ++i, ++i );
    // 102, 101
}

 TRACE の中でインクリメント。あ、これ見ると右のが先で、左のがあと
なんだね
この順番は開発環境によって違うから。 Visual C++ はこの順番ってだ

……具体的にゆーと、どうしてまずいん?
ひとつは、今の順番の問題。優先順位とか絡んで、複雑になる場合が多い
んだよね
 () を使えば……って、 TRACE() の中じゃ使えないか
そゆこと。 ?: でも使えないしね。こういうのは、1行ずつに分ければ解
決するでしょ
ってゆーと

void CDebugDlg::OnButton1() 
{
    int i = 100;
    ++i;
    TRACE( "%d, ", i );
    ++i;
    TRACE( "%d\n", i );
    // 101, 102
}

そういうこと。1行に1処理、その方が分かりやすいし。それに、コメン
トの関係もあるかな
コメント?
 // で1行まるごとコメントアウトしたとき、1行に詰め込まないほうが
無効になる処理の量が減るでしょ
そうすれば1機能ずつコメントアウトできる、ってことね
それにコンパイルエラーで行番号が出ても
詰め込んでるとどこがまずいのか分かりにくい、ね。確かに分かるんだけ
ど……書くのめんどいよ?
それはダメ! プログラム書くのに面倒とか考えちゃダメ
そーゆーもん?
そういうもの。こういう格言があります

今日の1秒、明日の1分。
今日の1分、明日の1秒。

明日の10分、じゃないの?
じゃないの。これは、1秒で手抜きして書いたコードは、後で問題が出た
ときに解決するのに1分掛かるけど、1分掛けてちゃんと書いたコードは、
後で問題が出ても1秒で解決する、って意味
最初に丁寧に書けば、後で楽ってことね
そもそもプログラムってそういうものだし
どゆこと?
別にさ、コンピューターの処理って人間がしなくてもいいものってあるで
しょ。計算とか
そりゃまぁ、そろばんでもいーんだろうけど
それをコンピューターで処理するって事は、今大変でも、後で楽だからで
しょ。一度プログラム作っちゃえば、それを使い回せばいいんだから
そういえばそうだね。ファイル検索とか、そういうもんだもんね。ってこ
とは、プログラムそのものがそういう性質なんだってこと?
ま、それがコンピューターのリズム、ってとこかな。それに合わせるのが
コツかも
コツねぇ……
それに、そんなに大変じゃないと思うよ
そなの?
複数行に書くときは、コピー&ペーストすればいいんだし
ってゆーか、コピー&ペーストするときって複数行の方が書きやすいよ

だね。あと手抜きしない方がいいのは、変数関係だね
変数?
まず変数の場所。プログラムが大きくなってくると、ついついクラスのメ
ンバ変数とかにしたくなるけど、たいがい普通の変数で間に合うから
ってゆーかあんま大きなプログラム作ったことないかも
それも大事! 小さく小さく作っていけば、変数で混乱することもないか
ら、だから普通の変数で十分なわけ
いつも言ってる〈小さい部分の積み重ねが大きなプログラムになる〉だよ

そういうこと。あと変数名も、横着せずにちゃんとね
最初に i とか付けるってヤツ?
それとは別の話なんだけど……これはちょっと違う話になるから、も少し
あとでまとめてするね。話を戻して、演算子関係であと関係がありそうなバ
グは、キャスト関係
 (int) とかね。あとこの前教えてもらった reinterpret_cast とか
そう。まず大事なのは、これからは (int) 形式の普通のキャストはしな
いようにすること
ん〜、具体的にはそれのまずさってなに?
まず、できちゃうことの多さ。普通のキャストは、整数値からポインタや
 const 剥がしを簡単にできちゃうでしょ
そういう、なんでもできちゃうってことがまずいんだ
そういうこと。ちょっと * を付けるか付けないかで全然違ってきちゃう
から
書き間違いの影響が大きい
そゆこと。その点
 static_cast reinterpret_cast const_cast dynamic_cast は機能が制限
されてるから安心
実際に例を見てみるとよくわかると思うよ。まずこのエラーの例

    const int i = 10;
    const char *pch = &i;   // コンパイルエラー。

 int のアドレスを char のポインタに、ってできないんだ
 int * と char * 、++ すると?
あ、進むバイト数が違うんだ
 int の中身を1バイトずつ操作したいときとかにこうするかな。これは
エラーが出るからキャストするんだけど、たとえばうっかり const のこと
を忘れちゃった場合

    const int i = 10;
    char *pch = (char *)&i; // const も剥がせちゃいました。

うん、キャストできちゃってるね
もし reinterpret_cast を使っていたら

    const int i = 10;
    char *pch = reinterpret_cast< char * >( &i );
    // コンパイルエラー。

 reinterpret_cast はポインタのキャスト専用だから、 const は剥がせ
ないんだ
そゆこと。そしてもうひとつの重要なメリット、それは検索しやすいって
こと。たとえば今の

    const int i = 10;
    const char *pch = reinterpret_cast< char * >( i );

 const 付けて直した以外に違うとこあるんだけど、わかる?
えーっと……あ、 i の前に & が着いてない!
でしょう。これだと i のアドレスじゃなく中身が渡されちゃって大問題
になります
でもコンパイルエラーになんないね
だからやっかい。でもこういうことすると実行時にはエラーになるわけ
それがバグってことね
バグが出たら探すことになるけど、そのときの候補としてまず上げるの
が、こういう〈キャストしてる部分〉
それを探すのに reinterpret_cast が便利って事ね
そゆこと。普通のキャストは型名そのままだから検索しにくいんだよね。
reinterpret_cast とかなら簡単に見つかるし、それに注意する部分も絞れ
るから

 reinterpret_cast ならポインタ関係、 const_cast なら const 関係っ
て絞れるでしょ
そっか、逆に言うと reinterpret_cast なら const 関係は考えないでい
いってことね
そゆこと。こういう部分でまめなコードを書けば、後々楽になるってわけ
です

/*
    Preview Next Story!
*/
でも reinterpret_cast って長いし間違えちゃうしめんどいよ?
間違えたってコンパイルエラーになるだけだからいいじゃん
そう? コンパイルエラーってかなり嫌なんだけど
大丈夫大丈夫、いつかコンパイルエラーが好きになる日が来るから
なんかヤダそれ
というわけで次回
< Version 6.14 オーバーフローに要注意! >
につづく!
優れたプログラマーはコンパイルエラーに愛される!
また変な格言出てきたし……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。