目次
2.17 ビットセグメントにおけるクロスプラットフォームの問題
I.はじめに
このセクションでは、友人に利益をもたらすことを期待して、構造、列挙、および関連付けに関する知識を友人と共有します。
2. テキスト
2.1 構造
2.11 構造の基礎知識
構造 体タグ{メンバー リスト ; _変数 リスト; _ _
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
匿名構造体は作成時の変数に対してのみ有効であることに注意してください。x、a[20]、*p は匿名であり、後で新しい構造体変数を作成するために匿名構造体を使用することはできません。
2.12 構造メモリの調整 (一般的なテスト ポイント)
次に、構造体のメモリ サイズを 詳しく見てみましょう。
//计算出结构体内存大小
struct S1
{
char c1; //1字节
int i; // 4字节
char c2; // 1字节
};
// 结果: 12个字节
どうしてこれなの?
ここで調べてみましょう
構造メモリのアライメント規則:
- 1.最初のメンバーは、構造体変数からのオフセットが0であるアドレスにあります。
- 2.他のメンバ変数は、ある数値(アライメント番号)の整数倍のアドレスにアライメントされている必要があります。
Alignment = コンパイラのデフォルトのアライメントとメンバー サイズの 小さい値 。VS のデフォルト値は 8 で、Linux のデフォルトのアライメント番号はそれ自体です。たとえば、次のようになります。 int アライメント番号は 4
- 3.構造体の合計サイズは、最大アライメント番号の整数倍です (各メンバー変数にはアライメント番号があります)。
- 4.構造がネストされている場合、ネストされた構造はその最大アライメント数の整数倍にアライメントされ、構造全体のサイズは最大アライメント数 (ネストされた構造のアライメント数を含む) の整数倍に等しくなります。 。
ここで前の例を分析します。
それを検出するにはどうすればよいですか?
ここでは、マクロオフセットを 紹介します。その機能は、構造体のメンバーが配置されているメモリのオフセットを返すことです。
ヘッダー ファイル: #include<stddef.h>
使用:
#include<stddef.h>
#include<stdio.h>
struct S3
{
double d;// 对齐数 8
char c; // 对齐数1
int i; // 对齐数4
}s3;
int main()
{
printf("%u\n", offsetof(struct S4, c1)); // 0
printf("%u\n", offsetof(struct S4, s3)); // 8
printf("%u\n", offsetof(struct S4, d)); // 12
return 0;
}
上の例では最初の 3 つの原則のみを実践していますが、入れ子構造の別の例を見てみましょう。
//练习4-结构体嵌套问题
struct S3
{
double d; // 对齐数 8
char c; // 对齐数1
int i; // 对齐数4
};
// S3 的大小: 16个字节
struct S4
{
char c1;
struct S3 s3; // 对齐数 8 , 16字节
double d; // 对齐数8
};
printf("%d\n", sizeof(struct S4));
// 结果: 32
2.13 構造メモリのアライメントが必要なのはなぜですか?
その多くは次のように説明されています。
- 1.プラットフォーム理由(移植理由):
すべてのハードウェア プラットフォームが任意のアドレスの任意のデータにアクセスできるわけではありません。一部のハードウェア プラットフォームは、特定のアドレスで特定の種類のデータしかフェッチできず、そうでない場合はハードウェア例外がスローされます。
- 2.パフォーマンス上の理由:
データ構造 ( 特にスタック )は 、できる限り自然な境界上に配置する必要があります。その理由は、unaligned にアクセスするためです。メモリを使用する場合、プロセッサは 2 回のメモリ アクセスを必要としますが、アライメントされたメモリ アクセスでは 1 回のアクセスのみが必要です。一般に:構造の記憶の調整は、 空間 と時間 を交換することです 。
では、どうすればスペースの使用量を削減できるのでしょうか?
狭いスペースにいるメンバーはできるだけ集まるようにしましょう。
2.14 デフォルトのアライメント番号を変更する
好き:
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1; // 1 8 -> 1
int i; // 4 8-> 4
char c2; // 1 8-> 1
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1) //设置默认对齐数为8
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;
}
#pragma Pack() によって変更されるデフォルトのアライメント番号は通常 2 ^ n であり、奇数が存在することはほとんどないことに注意してください。
2.15 関数パラメータの受け渡し
2.16ビットセグメント
1. ビットフィールドのメンバーは、 int 、 unsigned int または signed intでなければなりません 。2. ビットフィールドのメンバー名の後にコロンと数字があり、占有ビットを示します。
struct A
{
int _a:2; // 2 bit
int _b:5; // 5bit
int _c:10;
int _d:30;
}; // 问那 struct A的大小是多少?结果是 8个字节
//一个例子
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//空间是如何开辟的?
解析:
アイデア: まず、バイトがいっぱいであるか不十分な場合は、別のバイト領域を作成します。
ここで、以前のビッグ エンディアンとスモール エンディアンのバイト オーダーと区別する必要があり、今回は移動単位がビット、後者の単位がバイトになります。
2.17 ビットセグメントにおけるクロスプラットフォームの問題
1. intビットフィールドが 符号付き数値 とみなされるか、符号なし数値とみなされるかは不明です。2. ビットフィールドの 最大ビット数は 決定できません。( 16 ビット マシンの場合 は最大16、32ビット マシンの場合 は最大 32、27と記述されます。16ビットマシンでは問題が発生します。3. ビット セグメントのメンバーがメモリ内で左から右に割り当てられるか、右から左に割り当てられるかはまだ定義されていません。4. 構造に 2 つのビット セグメントが含まれており、2 番目のビット セグメントが大きすぎて最初のビット セグメントの残りのビットを収容できない場合、残りのビットを破棄するか利用するかがわかりません。
2.2 列挙
ある状況で考えられる結果をまとめて列挙し、使用する際にはその状況に適したデータをそのまま使用することです。
好き:
12 か月を 1 つのリストにすることができます
性別も1つずつ記載可能
次のように使用します。
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
enum a
{
c, // 默认值为 0
b, // 1
k = 200, //赋值为200
z, // 由前+ 1,为 201
t // 同理 202
};
2. 21 列挙の利点
- なぜ列挙型を使用するのでしょうか?
- 1.コードの可読性と保守性を向上させます (#define を減らします)。
- 2. #defineで定義された識別子と比較して、列挙型にはより厳密な型チェックが行われます。
(C++ では、列挙定数が割り当てられると、右側のデータが列挙され、列挙メンバーであるかどうかが判断されます)
- 3.命名汚染を防止します (名前の変更を避けるために、範囲を列挙名によって制限できます)
- 4.デバッグが簡単 (プリコンパイル中に置き換えられる #define 定義とは異なり、デバッグして対応する値を見つけることができますが、ソースを追跡するのは簡単ではありません)
- 5.使いやすく、一度に複数の定数を定義可能
2.3 コンソーシアム
#include<stdio.h>
union p1
{
char i;
int z;
}a;
int main()
{
printf("%p\n", &(a.i));
printf("%p\n", &(a.z));
return 0;
}
結果:
- 共用体のメモリ サイズも、構造メモリのアライメント原則に従う必要があります。
#include<stdio.h>
union p1
{
char i;
int z;
}a;
int main()
{
a.i = 5;
a.z = 1;
a.i == 1 ? printf("小端") : printf("大端");
return 0;
}
結果はリトルエンディアンで、char i を使用するとデータの最初のバイトしか読み取れず、int z で変更された値が最初のバイトに存在すると判断できるため、マシンはリトルエンディアンになります。
3. 結論
このセクションはここで終了です。閲覧していただいた友人に感謝します。ご提案があれば、コメント欄にコメントしてください。