コンテンツ
一般的に:構造のメモリアライメントは、時間と空間を交換する方法です。
ビットフィールドの宣言と構造は似ていますが、2つの違いがあります。
1.ビットフィールドのメンバーは、int、unsignedintまたはsignedint、charである必要があります
2.ビットフィールドのメンバー名の後にコロンと数字が続きます。
1.構造
1.1構造の宣言
構造体は、メンバー変数と呼ばれる値のコレクションです。構造体の各メンバーは、異なるタイプの変数にすることができます。
struct tag
{
member-list;
}variable-list;//变量列表,在这里创建的是全局变量
学生について説明する
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
1.2特別な宣言匿名の構造体
struct
{
char name[20];
char id[12];
}*ps;//必须在变量后加列表,只能使用一次
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
p = &x;//类型一样么?
匿名の構造体メンバーと同様に、コンパイラーにとっても異なるタイプの構造体です。
役割:作成された構造は1回だけ使用され、匿名の構造を使用できます
1.3構造体の自己参照typedefは型名変更です
間違った自己参照
struct Node
{
int data;
struct Node next;
};
正しい自己参照:次の構造体ポインターが含まれています
struct Node
{
int data;
struct Node* next;
};
1.4typedefタイプの名前変更
typedef struct Node
{
int data;//数据
struct Node* next;//指针
} Node,* pNode;
typedef对
//struct Node
//{
// int data;//数据
// struct Node* next;//指针
//};
类型重定义,取名为Node
pNode 等价--> struct Node*,对结构体指针重命名pNode,是一种指针类型
int main()
{
struct Node n1;//两个类型相等
Node n2;
return 0;
}
1.5構造変数の定義と初期化
struct Book
{
char name[20];
float price;
char id[12];
}s = { "投币", 55.5f, "PGC001" };
struct Node
{
struct Book b;
struct Node* next;
};
int main()
{
struct Book s2 = { "点赞", 66.6f, "HG001" };
struct Node n = { {"C语言拯救者", 66.8, "TG001"}, NULL };//结构体嵌套
return 0;
}
1.6構造体のメモリアライメント構造体のサイズを計算する
struct S1
{
char c1;//1个字节
int i;//4个字节
char c2;//1个字节
};
struct S2
{
char c1;//1
char c2;//1
int i;//4
};
int main()
{
struct S1 s;
struct S1 s2;
printf("%d", sizeof(s));
printf("%d", sizeof(s2));
return 0;
}
s構造体のサイズは12バイトであり、s2構造体のサイズは8バイトです。これは、構造体がメモリー整列されているためです。
構造体の配置規則:1。最初のメンバーは、構造体変数からのオフセットが0のアドレスに保管されます。
2.他のメンバー変数は、数値(アライメント番号)の整数倍のアドレスにアライメントする必要があります。
Alignment=コンパイラのデフォルトの配置とメンバーのサイズの小さい方。
VSのデフォルトのアライメント値は8です。Linxu環境にはデフォルトのアライメント番号はありません。デフォルトのアライメント番号がない場合、それ自体のサイズがアライメント番号になります。
3.構造体の合計サイズは、最大アライメント番号の整数倍です(各メンバー変数にはアライメント番号があります)。
4.構造体がネストされている場合、ネストされた構造体はそれ自体の最大整列数の整数倍に整列され、構造体の全体のサイズはすべての最大整列数(ネストされた構造体の整列数を含む)の整数になります。 。
例を使って4つのルールを実際に説明しましょう
struct S1
{
char c1;//1个字节
int i;//4个字节
char c2;//1个字节
};
もう一つの例
struct S2
{
char c1;//1
char c2;//1
int i;//4
};
s4構造の入れ子の問題、s4の大きさはどれくらいですか?
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
1.7なぜメモリアライメントが存在するのですか?
一般的に:構造のメモリアライメントは、時間と空間を交換する方法です。
1.プラットフォームの理由(移植の理由):すべてのハードウェアプラットフォームが任意のアドレスのデータにアクセスできるわけではありません。一部のハードウェアプラットフォームは、特定のアドレスの特定のタイプのデータのみをフェッチできます。そうしないと、ハードウェア例外がスローされます。
2.パフォーマンス上の理由:データ構造(特にスタック)は、可能な限り自然な境界に揃える必要があります。その理由は、アラインされていないメモリにアクセスするには、プロセッサが2回のメモリアクセスを行う必要があるためです。アラインされたメモリアクセスには1回のアクセスしか必要ありません。
構造物を設計する際には、配置を満足させるだけでなく、スペースを節約する必要があります。その方法:スペースをあまりとらないメンバーをできるだけ集中させます。
1.8デフォルトのアライメント番号を変更する
VSのデフォルトのアライメント番号は8であり、デフォルトのアライメント番号を2の累乗に変更するのが最適です。
#pragma pack(1)//设置默认对齐数为1
struct S
{
char c;//1
double d;//8
};
#pragma pack()取消设置的默认对齐数,还原为默认
int main()
{
struct S s;
printf("%d\n", sizeof(s));//占9个字节
return 0;
}
1.9構造パラメータの受け渡し
パラメータを関数に渡すときは、パラメータをスタックにプッシュする必要があります。これにより、時間とスペースの面でシステムのオーバーヘッドが発生します。構造体オブジェクトが渡されると、構造体が大きすぎ、パラメータスタッキングのシステムオーバーヘッドが比較的大きくなるため、パフォーマンスが低下します。
struct S
{
int data[1000];
int num;
};
struct S s = {
{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
構造体にパラメータを渡すときは、構造体のアドレスを渡す必要があります。
2.ビットセグメント
ビットフィールドの宣言と構造は似ていますが、2つの違いがあります。
1.ビットフィールドのメンバーは、int、unsignedintまたはsignedint、charである必要があります
2.ビットフィールドのメンバー名の後にコロンと数字が続きます。
ビット(バイナリビット)、:数値は占有されているビット数を表します
struct A
{
int _a : 2;//_a这个成员只占2个bit位
int _b : 5;//_b这个成员只占5个bit位
int _c : 10;
int _d : 30;
};
struct AA
{
int _a;//INT_MIN ~ INT_MAX
int _b;
int _c;
int _d;
};//16
//16位机器 - sizeof(int) - 2
//32/64位机器 - sizeof(int) - 4
int main()
{
printf("%d\n", sizeof(struct A));//8
printf("%d\n", sizeof(struct AA));//16
return 0;
}
予防:
1.ビットセグメントの使用は必要に応じて設計されます。たとえば、メンバーが0 1 2 3の3つの状態のみを表現する場合、スペースを節約するために2ビットのみを割り当てる必要があります。
2.ビットセグメントにはメモリアライメントがなく、ビットセグメントは構造体です。
2.1ビットセグメントのメモリ割り当て
1.ビットセグメントのスペースは、必要に応じて4バイト(int)または1バイト(char)の形式で開かれます。
1/4バイトのスペースを開くのに十分なスペースがありません。1/4バイトのスペースを開き続けるのに十分ではありません
2.ビットセグメントには多くの不確実な要素が含まれます。ビットセグメントはクロスプラットフォームではありません。移植性に重点を置いたプログラムでは、ビットセグメントの使用を避けたり、クロスプラットフォームコードを記述したりする必要があります。
3.ビットフィールドのメンバーは、int unsigned int signed intまたはchar(整数ファミリに属する)タイプにすることができます。
struct A
{
int _a : 2;//_a这个成员只占2个bit位
int _b : 5;//_b这个成员只占5个bit位
int _c : 10;
int _d : 30;
};
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
printf("%d\n", sizeof(s));
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
2.2ビットセグメントに関するクロスプラットフォームの問題
1.intビットフィールドが符号付きまたは符号なしの数値として扱われるかどうかは不明です。
2.ビットセグメントの最大ビット数を特定できません。(16ビットマシンの最大数は16、32ビットマシンの最大数は32です。intビットセグメントが27と記述されている場合、16ビットマシンで問題が発生します。
3.ビットフィールドのメンバーがメモリ内で左から右に割り当てられるか、右から左に割り当てられるかは定義されていません。
4.構造体に2つのビットセグメントが含まれていて、2番目のビットセグメントのメンバーが大きすぎて最初のビットセグメントの残りのビットを収容できない場合、残りのビットを破棄するか使用するかは不明です。
2.3ビットセグメントのアプリケーション
ネットワーク伝送パッケージ内のデータ:ネットワークプロトコルスタック
3.列挙
名前が示すように、列挙は列挙です。可能な値をリストします。
月曜日から日曜日までは7日間に制限されており、1つずつリストすることができます。
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
性別:男性、女性、機密...
enum Sex//枚举类型
{
MALE,
FEMALE,
SECRET//{}中的内容是枚举类型的可能取值,也叫 枚举常量 。
};
int main()
{
enum Sex s = MALE;//枚举类型变量赋值
enum Sex s2 = FEMALE;//枚举的可能取值
return 0;
}
これらの可能な値にはすべて値があります。デフォルトは0から始まり、一度に1ずつ増加します。もちろん、定義時に初期値を割り当てることもできます。
列挙定数が割り当てられた後、前の値+ 1に従って、割り当て後に列挙変数の外部で値を変更することはできません
enum Color//颜色
{
RED,//0
GREEN,//1
BLUE//2
};
enum Color//颜色
{
RED=1,
GREEN,//2
BLUE//3
};
enum Color//颜色
{
RED=22,
GREEN=28,
BLUE//29
};
RED = 6;//错误
3.1列挙の利点
1.コードの可読性と保守性を向上させます
2. #defineで定義された識別子と比較して、列挙には型チェックがあり、より厳密です。
cppのエラー
enum Color c = 5;//一个是枚举类型,一个是int类型
3.ネーミング汚染の防止(カプセル化)
4.デバッグは簡単です (#define定義シンボルの置き換えは、定義されたシンボルをコードに直接置き換えることです)
5.使いやすく、一度に複数の定数を定義できます
4.コンソーシアム(ユニオン)
ユニオンも特別なカスタムタイプです。このタイプで定義される変数には一連のメンバーも含まれ、これらのメンバーが同じスペースを共有するという特徴があります(したがって、ユニオンはユニオンとも呼ばれます)。
共用体型の宣言
union Un
{
char c;//1
int i;//4
};
int main()
{
union Un u;
printf("%d\n", sizeof(u));
printf("%p\n", &u);
printf("%p\n", &(u.c));
printf("%p\n", &(u.i));
return 0;
}
Unは4バイトを占有し、u、uc、およびuiの3つのアドレスはまったく同じです。
iを変更すると、cの値も変更されます(ユニオンのメンバーは一度に1つだけを使用し、同時には使用しません)
u.c = 0x55;
u.i = 0;
4.1コンソーシアムを使用して大小の最終判断を変革する
int check_sys()
{
union Un
{
char c;
int i;
}u;
u.i = 1;
return u.c;//
}
int main()
{
if (1 == check_sys())
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
4.2ジョイントサイズの計算
計算ルール:
組合の規模は、少なくとも最大の組合員の規模です。
最大メンバーサイズが最大アライメント数の整数倍でない場合は、最大アライメント数の整数倍にアライメントする必要があります。
union Un1
{
char arr[5];//5
int i;//4
};
union Un2
{
short arr[7];//14 2
int i;//4 4
};
int main()
{
printf("%d\n", sizeof(union Un1));//8,进行了内存对齐
printf("%d\n", sizeof(union Un2));//16,看数组成员而不是数组
return 0;
}