C 言語のカスタム型 (構造体、列挙体、共用体)

「前方は渋滞していますが、最適なルートを進んでいます」 - AutoNavi Maps

記事ディレクトリ

1. 構造の種類

1. 構造型の定義

2. 構造体変数の初期化

3. 構造体型変数の定義

4. 構造メモリの調整

5. ビットセグメントを実現する構造

2. 列挙型

3. コンソーシアム型


  皆さんこんにちは、ジニンです。

  この記事ではC言語のカスタム型の内容を中心に説明します C言語のカスタム型には構造体型、列挙型、共用体型などが含まれます。

1. 構造の種類

  構造型は最も一般的なカスタム タイプです。構造型の最も一般的な定義と使用シナリオの例を次に示します。

たとえば、ta の名前、年齢、性別、学生番号などを含む学生データの構造タイプを定義します。

たとえば、本のタイトル、価格、著者などを含む構造タイプで本のデータを定義します。

1. 構造型の定義

  構造体型が main 関数の外部で定義されている場合、構造体はグローバル変数となり、構造体型が main 関数内で定義されている場合、構造体はローカル変数になります。

struct Stu
{
	char name[10];
	int age;
	char Sex[3];
	char Id[20];
};

2. 構造体変数の初期化

  順番に初期化する場合と順番に初期化しない場合の 2 つの場合のコードは次のとおりです。

	//按顺序初始化
	struct Stu s = { "纪宁",18,"男","123456789" };
	struct Stu S[2] = { {"纪宁",18,"男","123456789" },
						{"余微",18,"女","123456789" } };
	//不按顺序初始化,用  .操作符
	struct Stu d = { .name = "纪宁",.Id = "123456789",.Sex = "男",.age = 19 };

3. 構造体型変数の定義

struct Stu
{
	char name[10];
	int age;
	char Sex[3];
	char Id[20];
}h, H[2], z;

int main()
{
	//按顺序初始化
	struct Stu s = { "纪宁",18,"男","123456789" };
	struct Stu S[2] = { {"纪宁",18,"男","123456789" },
						{"余微",18,"女","123456789" } };
	//不按顺序初始化,用  .操作符
	struct Stu d = { .name = "纪宁",.Id = "123456789",.Sex = "男",.age = 19 };
	return 0;
}

  未初期化の場合、構造体変数sは構造体変数hと等価であり、構造体配列S[2]は構造体配列H[2]と等価であり、構造体変数dは構造体変数zと等価である。

4. 構造メモリの調整

  構造体のメモリ アライメントでは、構造体のメモリ サイズを計算します。

構造の配置規則:

  1. 最初のメンバは、構造体変数からのオフセットが 0 のアドレスにあります。

  2. その他のメンバ変数は、ある数値(アライメント番号)の整数倍のアドレスにアライメントされている必要があります。Alignment = コンパイラのデフォルトのアライメントとメンバー サイズの小さい値VS のデフォルト値は 8 です。Linux にはデフォルトのアライメント番号はなく、アライメント番号はメンバー自体のサイズです。 

  3. 構造体の合計サイズは、最大アライメント番号の整数倍です (各メンバー変数にはアライメント番号があります)。

4. 構造がネストされている場合、ネストされた構造はそれ自体の最大  。

struct S1
{
	char c1;
	int i;
	char c2;
};
//练习2
struct S2
{
	char c1;
	char c2;
	int i;
};
//练习3
struct S3
{
	double d;
	char c;
	int i;
};
//练习4-结构体嵌套问题
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	printf("%d\n", sizeof(struct S3));
	printf("%d\n", sizeof(struct S4));
	return 0;
}

コードの実行結果

 コンピューターが構造メモリのアライメントを実行するのはなぜですか

  1. プラットフォームの理由 (移植の理由): すべてのハードウェア プラットフォームが任意のアドレスのデータにアクセスできるわけではありません。一部のハードウェア プラットフォームでは、特定のアドレスで特定の種類のデータしかフェッチできず、そうでない場合はハードウェア例外がスローされます。

  2. パフォーマンス上の理由: データ構造 (特にスタック) は、可能な限り自然な境界上に配置される必要があります。その理由は、アライメントされていないメモリにアクセスするには、プロセッサが 2 回のメモリ アクセスを行う必要があるのに対し、アライメントされたメモリ アクセスには 1 回のアクセスしか必要ないためです

一般に、構造のメモリ調整は、空間と時間を交換する実践です

  構造物を設計する際には、配置を合わせてスペースを節約する必要がありますが、その方法としては、狭いスペースを占有する部材をできるだけ集めることです。

次の 2 つのコードに示すように、定義順序が異なると、占有メモリの結果がまったく異なります。

struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
}

そうすると、メモリを削減する方法をまとめると、狭いスペースを占有するメンバーをできるだけ集めることになります。

  デフォルトのアライメント番号が不適切な場合、 #pragma Pack() を使用してデフォルトのアライメント番号を変更できます。括弧内のパラメータはアライメント番号に変更される値です

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
    //输出的结果是什么?
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));
    return 0;
}

  コードの実行結果から、アライメント番号が異なると構造体が占有するメモリ空間も異なることがわかります。 

5. ビットセグメントを実現する構造

  ビット フィールドは IP パケットでよく使用されますビットフィールドの宣言は構造体と似ていますが、ビットフィールドは宣言時にメンバーに割り当てられる領域のサイズを直接指定し、単位はバイナリビットです。

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

上記のコードの意味は、2 ビットのメモリを _a に割り当て、5 ビットのメモリを _b に割り当てることです。 

コロンの後の数字は、int で定義された変数に割り当てられるメモリ領域です。 

  1. ビット フィールドのメンバーは、int unsigned int signed int または char (整数ファミリーに属する) 型にすることができます。

  2. ビットフィールドのスペースは、必要に応じて 4 バイト (int) または 1 バイト (char) の形式で解放されます。

  3. ビットセグメントは不確実な要素が多く、クロスプラットフォームではないため、移植性を重視するプログラムではビットセグメントの使用を避けてください

  一般に、ビット セグメントの利点は、メモリ領域を可能な限り節約し、必要なだけ割り当てを実現できることですが、ビット セグメントの欠点は明らかにメモリ割り当ての問題であり、これは適切ではありません。クロスプラットフォーム向け

2. 列挙型

  列挙とは、その名のとおり、取り得る値を 1 つずつ列挙することです。たとえば、私たちの実生活では次のようになります。

週は月曜日から日曜日までの 7 日間のみで、1 つずつリストできます。

性別:男性、女性、非公開、1人ずつ記載することも可能です。

月は12か月あり、1つずつリストすることもできます

列挙型は、以下で定義されているように、キーワード enum + カスタム名として記述されます。

#include<stdio.h>​
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 ずつ増加します. もちろん、列挙型を宣言するときに初期値を割り当てることもできます。

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

  列挙定数のみを列挙型変数に値を割り当てるために使用できるため、型の違いは生じません。次に例を示します。 

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

列挙型の利点

1. コードの可読性と保守性が向上します。
2. #define で定義された識別子と比較して、列挙型にはより厳密な型チェックが行われます。
3. デバッグが簡単 (#define で定義された定数はデバッグできません)
4. 使いやすく、一度に複数の定数を定義できます

3. コンソーシアム型

  ユニオンは、変数とメンバーを定義する方法が構造体の定義方法と似ている特別なカスタム タイプです。この型で定義された変数には一連のメンバーも含まれており、これらのメンバーが同じ空間を共有するという特徴があります (したがって、共用体は共用体とも呼ばれます)。すべてのメンバーがメモリ空間を共有するため、共用体変数のサイズは少なくとも共用体メンバーの中で最大のものになります。

  共用体タイプの記述方法は、次のように、キーワード ユニオン + カスタム名です。

union Un
{
	int i;
	char c;
};
int main()
{
	union Un un;
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));
	return 0;
}

  実行結果から、int型のメンバiとchar型のメンバcのアドレスは同じであることがわかり、スペースを共有する規則が証明されています。

  共用体型のサイズ計算規則:共用体型のサイズは少なくとも最大のメンバーのサイズである; 共用体型のサイズは最大アライメントの整数倍でなければならない(これは構造体と同じ)

ここに画像の説明を挿入

  ブロガーは長い間書いていますが、ブロガーを励ますために無料のトリプルコンボを与えることができれば、あなたのタイパンツは本当に辛いと思います!

おすすめ

転載: blog.csdn.net/zyb___/article/details/131730987