#pragma twice

KAB-studio > プログラミング > #pragma twice > 205 Version 11.05 signed char と unsigned char

#pragma twice 205 Version 11.05 signed char と unsigned char

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

 Version 11.05
signed char と unsigned char

「今回は signed と unsigned について説明します」
『それ自体は前にやったよね。符号があるのとないのって』
「 Version 4.06 ( No.056 ) で説明したように、 signed が符号あり、 
unsigned が符号なしです」
『でもさ、それってあんま関係ないと思うんだけど。だって文字でしょ?』
「それがおおありなんです。まず、その singed とかを説明したページにあ
るコードをもう一度試してみます」

void Compare()
{
    char ch = -1;
    unsigned char uch = ch;

    if( ch < uch )
    {
        TRACE( "%d < %u\n", ch, uch );
    }
}
// -1 < 255

「何も付けてないと signed と同じだから、 ch の方は signed です。で、
それと unsigned を比較すると」
『そうそう、同じ -1 でも、 unsigned だと 255 になっちゃうんだよね』
「あ、でも勘違いしないで欲しいんだけど、ビット的には ch も uch も同
じ 0xFF だからね」
『え、そうなの?』

    unsigned char uch = ch;

「ってした時には、実際には何も起きてません。こっちじゃなくて」

    if( ch < uch )

「このとき、 ch は 0xFF を -1 、 uch は 0xFF を 255 ってみなして」
『それで比較しちゃうわけね』
「言い換えると、これは < の機能ってことかも」
『機能??』
「そう。 < は signed か unsigned か調べて、それを元に整数値に変換し
て比較してるってこと」
『そういう機能が備わってる……?』
「この辺はちょっと難しいかもね。演算子ってただ計算をするだけじゃなく
て、いろんな機能もあるんです。そういうののひとつってところかな」
『うーん……』
「まぁそれは今は置いておいて、重要なのは signed か unsigned かで比較
したときの結果が変わっちゃうってこと」
『それって重要なの?』
「すごく重要。だって前回、 Shift JIS のリードバイトの範囲とトレイル
バイトの範囲って言ったでしょ」
『あーっ!!! そっか、その範囲に入ってるかどうかって、もちろん比較
しなきゃいけないわけだから……』
「では実際に試してみます。リードバイトかどうか判別する関数の signed 
を使うほうから」

bool IsReadByte_bad( signed char p_ch )
{
    if(
        ( ( 0x81 <= p_ch ) && ( p_ch <= 0x9F ) ) ||
        ( ( 0xE0 <= p_ch ) && ( p_ch <= 0xFC ) )
        )
    {
        // リードバイトです。
        return true;
    }

    return false;
}

「これを次のような関数から呼びます」

void Use_IsReadByte_bad()
{
    const signed char pchStr[] = "あ";
    bool bResult = IsReadByte_bad( pchStr[0] );
    if( bResult == true )
    {
        TRACE( "リードバイトです。" );
    }
    else
    {
        TRACE( "トレイルバイトです。" );
    }
    // トレイルバイトです。
}

「 "あ" のリードバイトを渡してるんだから、絶対にリードバイトになるは
ずなんだけど」
『げ、実行したらトレイルバイトって言われた』
「 "あ" の文字コードは 0x82 0xA0 」
『範囲には入ってるよねぇ』
「でも」

    0x81 <= p_pch

「この p_pch には 0x82 が入ってるわけだから、普通に比較すると」

    0x81 <= 0x82

『だからあってるはずよね』
「ところが、 p_pch は signed だから符号あり。そうなると、互いに符号
ありの比較になります。その場合」

    -127 <= -126

「となるため、当てはまらなくなってしまうんです」
『……質問!』
「はい火美ちゃん」
『 0x81 が -127 とかってどうやればわかる?』
「それはこうしてください」

    TRACE( "%d\n", 0xFFFFFF81 );
    // -127

『う、なんか F が多い』
「こうすると -127 が出てきます」
『なんで F 入れなきゃいけないの?』
「う”……それはかなーり難しいから今度」
『う”ー……あ、もひとつ質問』
「はい火美ちゃん」
『 signed で比較すると逆になっちゃったけど、それって16進数だと大き
いのに10進数だと小さいってことだよね。なんか変くない?』
「さっきのページで教えたでしょ。 -1 は 11111111 、つまり 0xFF 」
『あ、そっか、逆になるんだ』
「だから、実際にはこんな感じ」

16進数 : 0x00 - 0x7F - 0x80 - 0xFF
signed   :    0    127   -128     -1
unsigned :    0    127    128    255

「 signed だと、 0x7F と 0xF8 で逆になるって考えてください」
『むー、ややこしい……』
「まぁそういうわけで、この 0x7F 以上の数を使う文字の場合、 signed で
計算するとうまくいきません。なので、 unsigned を使います」

bool IsReadByte( unsigned char p_ch )
{
    if(
        ( ( 0x81u <= p_ch ) && ( p_ch <= 0x9Fu ) ) ||
        ( ( 0xE0u <= p_ch ) && ( p_ch <= 0xFCu ) )
        )
    {
        // リードバイトです。
        return true;
    }

    return false;
}

// 使います。
void Use_IsReadByte()
{
    const unsigned char pchStr[] = "あ";
    bool bResult = IsReadByte( pchStr[0] );
    if( bResult == true )
    {
        TRACE( "リードバイトです。" );
    }
    else
    {
        TRACE( "トレイルバイトです。" );
    }
    // リードバイトです。
}

『うん、こっちならちゃんとリードバイトって出た』
「あと、今回は省略するけど、どっちの文字の方が大きいか、っていうのを
比較するときにも関係するから」
『文字の大きさ?』
「たとえば」

ABC
BBC

「ってあった場合、辞書に並べるときはこの順番でしょ」
『そういう大きい小さい?』
「そう。こういう時 A < B みたいな比較をするわけだけど」
『これも unsigned じゃないとうまくいかないわけね』
「さて、ここまでを踏まえて、これからすごくややこしい話をします」
『う”っ、な、なに?』
「今見たように、 char は unsigned char とした方が安全です」
「 unsigned にすると出るデメリットはないの?」
『ありません』
「ならそうした方がいいわけ……よね」
『なんだけど、そうしません』
「そこがややこしいとこなんだ。なんで?」
「うーん、一番の理由は慣例的にそう、だからかな。 unsigned を付けない 
char しか使わない、っていうのが普通だし、問題になるのは文字の比較と
か四則演算するときくらいだから、その時キャストすればいいし」
『……二番目の理由は?』
「実は、 char が signed とは決まってないんです」
『へ?』
「 char が実際には signed か unsigned かは開発環境によって違うんで
す。 Visual C++ は char は signed 。 unsigned なのもあるし、最新の
VC .Net は好きな方を選べるしね」
『ならやっぱり、常に unsigned にするべきなんじゃない?』
「確かにそうなんだけどね……これがきっと最大の理由なんだけど、 
unsigned char から char へはキャストが必要なんだけど、ランタイムとか 
API とかは」
『げ。そっか、普通の char だから、渡すたびにキャストしなきゃいけない
んだ……』
「それはあまりにも大変だし、そうなると結局」
『何も付けない char 使うしかないってわけね……』

/*
    Preview Next Story!
*/
『なんだか、政治的策略を感じるわ……』
「そういう部分もあるかも」
『否定しないのー!?』
「こういうことは派閥争いと無縁じゃないから」
『そんでもって掲示板で罵り合うことになるんだ……ぶるぶる』
「というわけで次回」
< Version 11.06 Unicode >
『につづく!』
「今度もまたそういうのだ……」
『また!?』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。