カスタム タイプ: 構造体、列挙型、共用体 (2)

2. ビットセグメント

ビット セグメントの出現はスペースを節約するためです。

2.1 ビットセグメントとは何ですか?

ビット フィールドの宣言と構造は似ていますが、次の 2 つの違いがあります。
1. ビット フィールドのメンバーは、int、unsigned int、または signed int である必要があります。
2. ビットフィールドのメンバー名の後にコロンと数字があります。

例えば:

struct A
{
   int _a:2;
   int _b:5;
   int _c:10;
   int _d:30;
};

 A はビット フィールド タイプです。ビット フィールドのビットはバイナリ ビットを指します。メンバー名の後の数字は、スペースを占有するビット数を表します。_a は 2 ビット、_b は 5 ビットを占有します。
セグメント A のサイズはどれくらいですか?

このAが構造体だと16バイト以上になりますが、ビットフィールドだと8バイトになりますが、これはなぜでしょうか?

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{

    printf("%d\n", sizeof(struct A));
	return 0;
}

 2.2 ビットセグメントのメモリ割り当て

1. ビット フィールドのメンバーは、int unsigned int signed int または char (整数ファミリーに属する) 型です。
2. ビット フィールドのスペースは 4 ワードです。必要に応じてセクション(int)または1バイト(char)形式で開かれます。
3. ビット セグメントには不確実な要素が多く含まれます。ビット セグメントはクロスプラットフォームではないため、移植性を重視するプログラムではビット セグメントの使用を避ける必要があります。

 上記のコードを使用して空間を事前に開きます。メンバーはすべて int であるため、最初に整数型、4 バイト、32 ビットを開きます。次に、_a が最初に 2 ビットを占め、次にこれが 32 ビットになります は左から右に使用されますか、それとも右から左に使用されますか??これは不確実であるため、ビット セグメントはクロスプラットフォームではなく、コンパイラによって異なる可能性があります。 30 ビットを使用すると 2 ビットが残りますが、次のメンバーは 6 ビットを使用する必要があるため、残りの 2 ビットを使用する必要があります?使用しない場合は無駄になります。使用する場合は 4 ビットを開く必要がありますが、これも不確実です。

 

 たとえ不確実性がさらにあったとしても、それが vs. でどのように使用されるかを調査することはできます。

この構造体タイプ S では、a は 3 ビットを占め、b は 4 ビットを占め、c は 5 ビットを占め、d は 4 ビットを占めます。s が作成された後、値 0 が割り当てられます。a に 10 と b を入れます。a に 12 を入れます。 in it、a 3 in c、a 4 in d. 次に分析します。

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

メンバーはすべて char 型であるため、一度に 1 バイトずつ割り当てられます。まずメモリ内のバイトを開き、a の格納を開始します。A は 3 ビットを占有します。 が最初に下位ビットを使用し、a が 3 ビットを占有し、次に b が 4 ビットを占有し、次に左の 4 ビットを占有し、その後最初のバイトがまだ 1 ビット残っているとします。 c を格納するだけでは十分ではないため、もう一度バイトを開きます。最初のバイトの残りのビットを使用する場合、必要なのは 1 バイトだけですc と d を格納します。使用しない場合は、別のバイトを割り当てる必要があります。

それをテストして、それがどのようなものであるかを見てみましょう。結果は 3 なので、残りのバイトは無駄になります。

 

  a は 3 ビットしか使用できないため、2 進数では 10 の最後の 3 ビット、つまり 010、b は 1100、c は 00011、d は 0100 のみを格納できます。16 進数に変換すると、6、2、0 になります。 、3、0、4。

 

 

 デバッグの結果、これが当てはまることがわかり、推測は正しかったです。メモリは低いものから高いものへと使用されます。同時に、残りのスペースが次のメンバーに十分ではない場合、直接無駄になり、新しいスペースが追加されます。開かれています。

2.3 ビットセグメントにおけるクロスプラットフォームの問題

1. int ビット フィールドが符号付き数値とみなされるか、符号なし数値とみなされるかは未定義です。
2. ビット フィールドの最大ビット数は決定できません。 (16 ビット マシンの最大制限は 16 で、32 ビット マシンの最大制限は 32 です。27 と書き込むと、16 ビット マシンで問題が発生します。
3ビット セグメントのメンバーはメモリ内で左から右に割り当てられていますか? 右から左への割り当て標準は定義されていません。
4. 構造体に 2 つのビット フィールドが含まれる場合、 2 番目のビット フィールドのメンバーが大きく、最初のビット フィールドの残りのビットに収まらないため、残りのビットを破棄するか使用するかが不明です。

要約:

構造と比較すると、ビット セグメントは同じ効果を達成でき、スペースを大幅に節約できますが、クロスプラットフォームの問題があります。

2.4 ビットセグメントの適用

データを送信する際には、データをカプセル化する必要があり、上記のように一連のデータをデータ上でラップする必要がある場合があります。

上記のようなデータに直面した場合、正確に 32 ビットでスペースを節約できるビット セグメントを使用すると非常に便利です。

データが比較的大きい場合はネットワークへの負担が比較的大きく、データが比較的小さい場合は、実際に送信するデータ以外のデータは非常に小さいため、ネットワークへの負担は比較的小さく、そして効率は比較的高いです。


3. 列挙

列挙は、その名前が示すとおり、1 つずつリストします。
可能な値を 1 つずつリストします。
たとえば、私たちの実生活では次のようになります。

週には月曜日から日曜日までの 7 日間の数が限られており、1 つずつリストできます。
性別には、男性、女性、機密情報が含まれます。1 つずつリストすることもできます。
月は 12 か月あり、1 つずつリストすることもできます。

ここでは列挙を使用できます。

3.1 列挙型の定義

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};
enum Color//颜色
{
	RED,
	GREEN,
	BLUE
};

上記で定義された enum Day 、 enum Sex 、および enum Color はすべて列挙型です。
{} 内の内容は、列挙型の可能な値であり、列挙定数とも呼ばれます。
これらの可能な値はすべて有効で、デフォルトでは 0 から始まり、一度に 1 ずつ増加します。

したがって、main関数内の定数値を変更したい場合、定数であるため変更できません。


もちろん定義時に初期値を与えることも可能です。例えば:

enum Color//颜色
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};

3.2 列挙型の利点

なぜ列挙型を使用するのでしょうか?

#define を使用して定数を定義できますが、なぜ列挙型を使用する必要があるのでしょうか?

列挙型の利点:
1. コードの可読性と保守性の向上
2. #define で定義された識別子との比較 列挙型には型チェックがあります。より厳格に。
3. ネーミング汚染を防止 (カプセル化)
4. デバッグが簡単
5. 使いやすく、複数の定義を使用可能1 つの時定数で定義

 3.3 列挙型の使用

列挙型変数の値は変更できませんが、最初に列挙型変数に値を割り当てることはできます。

enum Color//颜色
{
  RED=1,
  GREEN=2,
  BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

それでは、列挙型変数が占有するメモリ サイズはどれくらいでしょうか?

enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};
int main()
{
	enum Sex sex = FEMALE;
	printf("%d\n", sizeof(sex));
	return 0;
}

 

 実際、列挙変数のサイズは整数のサイズです。


4. ユニオン(コミュニティ)

4.1 共用体型の定義

Union も特殊なカスタム タイプです
このタイプで定義された変数には、一連のメンバーも含まれます。特徴は、これらのメンバーが同じ空間を共有することです (したがって、union も呼ばれます)共有ボディ)。
例:

//联合类型的声明
union Un
{
	char c;
	int i;
};
//联合变量的定义
union Un un;

共用体のサイズを計算したい場合、int は 4 バイト、char は 1 バイトになります。答えは次のようになりますか?

4.2 組合の特徴

共用体のメンバーは同じメモリ空間を共有するため、共用体変数のサイズは少なくとも最大のメンバーのサイズでなければなりません (共用体は少なくとも最大のメンバーを格納できなければならないため)。

共用体のサイズを計算したい場合、int は 4 バイト、char は 1 バイトになります。答えは次のようになりますか?

union Un
{
	char c;
	int i;
};
int main()
{
	union Un un;//联合变量的定义
	printf("%d\n", sizeof(union Un));
	return 0;
}

 どうしてこれなの?

国連とその加盟国の住所を印刷すると、住所が同じであることがわかります。

union Un
{
	char c;
	int i;
};
int main()
{
	union Un un;//联合变量的定义
	printf("%p\n", &un);
	printf("%p\n", &(un.c));
	printf("%p\n", &(un.i));
	return 0;
}

 

i は 4 バイト、c は 1 バイトを占め、アドレスは同じなので、i の最初のバイトと c の最初のバイトは同じ空間ですか?つまり、空間を共有するということでしょうか?

 答えは正しいので、組合はコミュニティとも呼ばれます。ただし、それらは相互に影響し、一方を変更すると他も変更されます。

したがって、ユニオンのサイズは、データを保存できる前に少なくとも最大のメンバーのサイズになります。

それでは、コンソーシアムの用途は何でしょうか?

共用体を使用して、現在のマシンがビッグ エンディアンであるかリトル エンディアンであるかを判断できます。データの下位データがメモリの下位アドレスに配置される場合をリトルエンディアンストレージと呼び、データの下位データがメモリの上位アドレスに配置される場合をビッグエンディアンストレージと呼びます。 -エンディアンストレージ。

以前、以下のような判定コードを書きました。

int check_sys()
{
	int a = 1;
	if (*(char*)&a == 1)
		return 1;
	else
		return 0;
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

次のように簡略化します。

int check_sys()
{
	int a = 1;
	return *(char*)&a;//返回1表示小端,返回0表示大端
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

ここで、union を使用して判断します。

まず、共用体変数 u を作成します。i に値 1 を代入すると、c の値を取得するということは、i のデータの最初のバイトを取得することを意味し、return c で結果を取得できます。

int check_sys()
{
	union
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;//返回1表示小端,返回0表示大端
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

4.3 ユニオンサイズの計算

結合のサイズは、少なくとも最大のメンバーのサイズです。
最大メンバー サイズが最大アライメント数の整数倍ではない場合、最大アライメント数の整数倍にアライメントする必要があります。

例えば:

Un1 では、c は 5 バイト、i は 4 バイトですが、Un1 が 8 バイトなのはなぜですか? 最大メンバー サイズが最大アライメント数の整数倍ではない場合、最大アライメント数の整数倍にアライメントする必要があります。 c は配列です。最大アライメント数は要素ごとに計算されます。これは 5 つの char 型変数を配置することに相当します。この場合、最大アライメント数は 1 となり、i の最大アライメント数は 4 になります。の場合、3 バイト、つまり 8 バイトが無駄になります。

Un2 では、c は 7 バイト、i は 4 バイトですが、なぜ Un2 は 16 バイトなのでしょうか? cはshort型の配列で、最大アライメント数は2、iの最大アライメント数は4ですが、cは14バイトを占有するので16となります。

union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
int main()
{
	printf("%zd\n", sizeof(union Un1));
	printf("%zd\n", sizeof(union Un2));
	return 0;
}

コンソーシアムの利用条件は非常に厳しいのですが、どのような場合に利用すればよいのでしょうか?

その場合、一部のメンバーは同時に使用されなくなります。


本日のシェアはここまでです!読んでいただきありがとうございます。また次号でお会いしましょう。

おすすめ

転載: blog.csdn.net/2301_79035870/article/details/133816224