developer's diary

最近はc#のエントリが多いです

C# (dotnetcore) .netの内部実装を覗いてbit演算子の使い方を学んでみよう

前提

c#のboolは1Byte

サイズを確認する場合、sizeof演算子を使って確認できる

Console.WriteLine("boolのサイズ:" + sizeof(Boolean));

実際、boolのサイズは、プラットフォームに依存するらしい。

(ほぼほぼ1Byteでしょうが、1Byteと明記されているオフィシャルは見つかりませんでした・・・)

c#のintは4byte

下記のロジックで確認すると、c#ではint型は4Byte。

Console.WriteLine("intのサイズ:" + sizeof(int));

bitの使い所

保存するデータを極力小さくしたい場合に利用する という選択肢が考えられます。組込系とか大量のデータを保存する場合とか。

1億件の情報を保存する

32項目のフラグをboolで保存すると、32Byte使いますが、intを利用すると、4Byteですみます。

以下の表は、32項目のフラグをboolとintで保存した場合の総容量の違いです。

バイト数 最低必要なバイト数
bool 32Byte 32,000,000,000
int 4Byte 400,000,000

boolを保存した場合、3Gbyteを超える容量です。

intだと400MByteで済みますね。

ゲームなどはとんでもない量のデータを扱うと思いますので、bit演算が多様されると想像できます。

.netのリファレンス 実装(referencesource)を覗いても、結構使われたりすることが分かります。

.netのリファレンス 実装(referencesource)を覗く

さて本題です。覗いてみます。覗いてみるって少しエロチックな言葉ですね。 鶴の恩返しのようです。が、安心してください。 覗いてもどこかへ行きません。多分(汗)

.netのリファレンス ソースとは

.netのリファレンス ソースとは.netの内部のコードって意味ですね。

元々、c#という言語仕様は国際的な標準化団体で公開されていたのですが、

プログラム自体の中身はブラックボックスだったんですが、

今では公開されているので、windowsフォームを利用したシステム開発でちょくちょく覗いていました。

.netのリファレンス ソースは、Microsoftがどのようにプログラミングしているかを見ることができるため、「学び」にとても役立ちます。

ページの最後にリンクを貼り付けておきます。

Controlクラスでbit演算子を使っているところを覗いてみる

Windowsフォームで必ず使う、Controlクラスを覗いてみる。

フラグを格納する変数の定義

private int                           state;                  // See STATE_ constants above

フラグの定義

        internal const int STATE_CREATED                = 0x00000001;
        internal const int STATE_VISIBLE                = 0x00000002;
        internal const int STATE_ENABLED                = 0x00000004;
        internal const int STATE_TABSTOP                = 0x00000008;
        internal const int STATE_RECREATE               = 0x00000010;
        internal const int STATE_MODAL                  = 0x00000020;
        internal const int STATE_ALLOWDROP              = 0x00000040;
        internal const int STATE_DROPTARGET             = 0x00000080;
        internal const int STATE_NOZORDER               = 0x00000100;
        internal const int STATE_LAYOUTDEFERRED         = 0x00000200;
        internal const int STATE_USEWAITCURSOR          = 0x00000400;
        internal const int STATE_DISPOSED               = 0x00000800;
        // 省略

フラグを確認する様子

フラグを判定する場合、&(AND:論理積)を利用している

        public bool Created {
            get {
                return(state & STATE_CREATED) != 0;
            }
        }

フラグを立てている様子

フラグを立てる場合、|(OR:論理和)を使う。

state |= STATE_CREATED;

フラグをおろしている様子

フラグを下ろす場合、&(AND:論理積)と~(NOT:否定)を使う。

state &= (~STATE_CREATED);

サンプルプログラム

using System;

namespace bit
{
    class Program
    {
        enum Flgs
        {
            None = 0b0000,
            flg1 = 0b0001,
            flg2 = 0b0010,
            flg3 = 0b0100,
            flg4 = 0b1000
        }

        static void Main(string[] args)
        {
            Console.ReadKey();

            Flgs flgs = new Flgs();

            Console.WriteLine("Flgsのサイズ" + sizeof(Flgs));

            //フラグを全て下ろす
            flgs = Flgs.None;

            //フラグの状態をチェック
            CheckFlg(flgs, Flgs.flg1, nameof(Flgs.flg1));

            //flg1のフラグを立てる
            flgs = flgs | Flgs.flg1;

            //フラグの状態をチェック
            CheckFlg(flgs, Flgs.flg1, nameof(Flgs.flg1));

            //flg1のフラグを下ろす
            flgs = flgs & ~Flgs.flg1;

            //フラグの状態をチェック
            CheckFlg(flgs, Flgs.flg1, nameof(Flgs.flg1));

            Console.ReadKey();
        }

        /// <summary>
        /// フラグをチェックする関数
        /// </summary>
        /// <param name="flgs">フラグを設定した変数</param>
        /// <param name="flgs2">チェックしたいフラグを設定</param>
        /// <param name="flgName">フラグの名称を設定</param>
        static void CheckFlg(Flgs flgs, Flgs flgs2, string flgName)
        {
            if ((flgs & flgs2) != 0)
            {
                Console.WriteLine(flgName + "が立っている");
            } else
            {
                Console.WriteLine(flgName + "が降りている");
            }
        }
    }
}

参考

www.itmedia.co.jp

www.pursue.ne.jp

qiita.com

qiita.com

referencesource.microsoft.com