Xiao Wang のコード学習ブログへようこそ
文字関数の次に学ぶ必要があるのは、カスタム型の構造、列挙、および共用体です。
目次
序文
C 言語には組み込みデータ型とカスタム型があることがわかっています。詳細については、[C 言語上級] メモリ内のデータ ストレージの詳細な分析_Xiaowang Xuecode ブログ - CSDN ブログを参照してください。
次に、カスタム型の構造体、列挙型、共用体とは何なのか、またその使い方について詳しく紹介していきます。
1. 構造
構造体はメンバー変数と呼ばれる値の集合であり、構造体の各メンバーは異なる型の変数にすることができます
1.1 構造体の宣言
実践的なコードのデモ:
struct Stu {//学生结构体
char name[20];//姓名
int age;//年龄
char id[20];//学号
};//分号是自带的,不能丢失,
int main()
{
struct Stu stu = { "name",12,"123456" };
return 0;
}
1.2 特別な宣言と構造的自己参照
構造体を宣言する場合、不完全な宣言を使用できますが、現時点では可能ですが、セミコロンの前にグローバル変数を設定する必要があります。そうしないと、構造体 (匿名構造体) が見つかりません。
デモ:
struct {
int age;
char name[20];
}x;
//是可以这样没有标签的,但是前提是创建x这样的全局变量,否则之后就找不到了
//因为是匿名结构体
struct {
int age;
char name[20];
}a[10],*p;
int main()
{
scanf("%s", x.name);
printf("%s", x.name);
p = &x;
//非法的,因为p和x不是指向同一个结构体类型的,虽然成员变量一样,但是实际上类型是不同的
return 0;
}
次に、上記のコードの 1 番目と 2 番目の構造が同じであるかどうかを考えてみましょう。p =&x は成立しますか?
グラフィック:
上で説明したように、コンパイラは上記 2 つの構造体の宣言を 2 つの完全に異なる型として扱うため、不正です。
1.4 構造の自己参照
構造体の自己参照は、配列リストや連結リストなどのデータ構造側でよく使われます。 次に紹介します。この宣言を構造体の自己参照と呼びます。
構造体の自己参照は、その名前が示すように、構造体内で構造体型のメンバー変数を使用します。
グラフィック:
コードデモ:
//正确的自引用方式 有*号
struct Node
{
int data;
struct Node* next;
};
//错误的方式 没有*号
struct Node
{
int data;
struct Node next;
};
1.5 構造体変数の定義と初期化
上記では宣言方法を紹介しました。次に、構造体の使用方法、初期化方法、定義方法を見てみましょう。
コードデモ:
struct Point {
int x;
int y;
}p1; //声明类型的同时进行定义变量p1 这个是全局变量
struct Point p2; //这也是定义结构体变量,在结构体外面,函数外面,这也是全局变量
//初始化,定义变量的同时进行赋初值
struct Point p3 = { 1,2 };
struct Stu {
char name[20];//姓名
int age;//年龄
};
struct Stu s = { "why",20 };//初始化,这也是全局变量
struct Node {
int date;
struct Point p;
struct Node* next;
}n1={10,{1,2},NULL}; //结构体嵌套初始化
struct Node n2 = { 10,{1,2},NULL };//结构体嵌套初始化
残りを割り当てて初期化する方法:
struct Point
{
int x;
int y;
}p1 = {10, 20};
struct Point p2 = {0,0};
struct S
{
int num;
char ch;
struct Point p;
float d;
};
int main()
{
struct Point p3 = {1,2};
struct S s = { 100, 'w', {2,5}, 3.14f};
//这些属于基础的部分,赋值需要使用大括号,内部如果有其他类型的结构体,也要进行使用大括号
struct S s2 = {.d=1.2f, .p.x=3,.p.y=5, .ch = 'q', .num=200};
//创建一个变量之后,可以使用 .d=1.2f 这样的形式进行赋值,可以不用考虑结构体成员变量的顺序
printf("%d %c %d %d %f\n", s.num, s.ch, s.p.x, s.p.y, s.d);
printf("%d %c %d %d %f\n", s2.num, s2.ch, s2.p.x, s2.p.y, s2.d);
return 0;
}
1.6 構造メモリの調整
その上。構造体の基本的な使い方をほぼ理解しました。
データ型がメモリ内のスペースを占有することは誰もが知っています。構造体のサイズの計算について詳しく見てみましょう。
これは非常に重要な知識ポイントです:構造メモリの調整
記憶の調整とは何ですか? どのように調整されていますか? 以下のルールがあります
1.最初のメンバは、構造体変数からのオフセットが 0 のアドレスにあります。
2. その他のメンバ変数は、ある数値(アライメント番号)の整数倍のアドレスにアライメントされている必要があります。
Alignment = コンパイラのデフォルトのアライメントとメンバー サイズの小さい値。
VS のデフォルト値は 8 3 です。
構造体の合計サイズは、最大アライメントの整数倍です(各メンバー変数にはアライメントがあります) 。4.構造がネストされている
場合、ネストされた構造はそれ自体の最大アライメントの整数倍にアライメントされ、構造全体のサイズはすべての最大アライメント (ネストされた構造のアライメントを含む) 倍の整数になります。
コードデモ:
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));
return 0;
}
グラフィック分析の説明:
なぜメモリアライメントが存在するのでしょうか?
説明する:
1. プラットフォームの理由 (移植の理由):
すべてのハードウェア プラットフォームが任意のアドレスのデータにアクセスできるわけではありません。一部のハードウェア プラットフォームは、特定のアドレスで特定の種類のデータしかフェッチできません。そうでない場合は、ハードウェア例外がスローされます。
2. パフォーマンス上の理由:
データ構造 (特にスタック) は、可能な限り自然な境界上に配置される必要があります。
その理由は、アライメントされていないメモリにアクセスするには、プロセッサが 2 回のメモリ アクセスを行う必要があるのに対し、アライメントされたメモリ アクセスには 1 回のアクセスしか必要ないためです。
つまり、構造の記憶の調整は、空間と時間を交換する実践なのです。!!
したがって、将来の構造設計では、位置合わせを満たし、スペースを節約するために最善を尽くす必要があります。
狭いスペースを占めるメンバーはできるだけ集まるようにしましょう!!!
1.7 デフォルトのアライメント番号を変更する
#pragma preprocessing ディレクティブを使用して、デフォルトのアライメントを変更できます (デフォルトのアライメントはコンパイラによって異なります)。
#pragma Pack(num) デフォルトのオフセットを num に変更します
#pragma Pack () は、以前に設定されたデフォルトのオフセットをキャンセルし、コンパイラのデフォルトのオフセットに戻すことを意味します
コードデモ:
#pragma pack(4)//设置默认对齐数为4
struct s3 {
char c1;
int i;
char c2;
double c;
};
#pragma pack()//再次使用这个预处理宏,会取消设置的默认对齐数
#pragma pack(1)//设置最大对齐数为1
struct s4 {
char c1;
int i;
char c2;
double c;
};
int main()
{
printf("%d ", sizeof(struct s3));
printf("%d", sizeof(struct s4));
return 0;
}
したがって、#pragma Pack() の使い方を学んだ後は、構造体のアライメントが適切でない場合に、デフォルトのアライメント番号を自分で変更できます。
1.8 構造体パラメータの受け渡し
パラメーターを渡す方法は 2 つあります。最初のパスの構造体と 2 番目のパスのアドレスです。どちらの方が良いでしょうか?
コードデモ:
//传参:1.传递结构体,2.传递地址
struct s3 {
int data;
};
struct s3 s = { 1000 };
void print1(struct s3 s)//传递结构体
{
printf("%d ", s.data);
}
void print2(struct s3* s) {//传递地址
printf("%d", s->data);
}
int main() {
print1(s);//传递结构体
print2(&s);//传递地址
}
2 つの方法では、アドレスが渡されるため、仮パラメータの作成時にスペースが再度適用されることはありません。また、構造体のメモリは一般に比較的大きいため、コストをよりよく節約するために、アドレスを渡すことを好みます。
1. 関数がパラメータを渡すとき、パラメータをスタックにプッシュする必要があるため、時間と空間のシステム オーバーヘッドが発生します。
2. 構造体オブジェクトが渡されると、構造体が大きすぎてパラメータをスタックにプッシュするシステムのオーバーヘッドが比較的大きくなり、パフォーマンスの低下につながります。
2、列挙
1. 列挙の定義
列挙とは、あらゆる可能性を一つ一つ列挙することです。
可能なすべての値を 1 つずつリストします。たとえば、週 7 日など、月曜日、火曜日... をリストすることができます。
コードデモ:
enum Day//星期
{
Mon,
Tues, //枚举成员之间使用逗号隔开
Wed,
Thur,
Fri,
Sat,
Sun //且枚举的成员的取值是从0开始的,逐渐+1
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN,
BLUE
};
int main()
{
printf("%d\n", Mon);
printf("%d\n", Tues);
printf("%d\n", Wed);
printf("%d\n", Thur);
printf("%d\n", Fri);
printf("%d\n", Sat);
printf("%d\n", Sun);
return 0;
}
上記で定義された enum Day 、 enum Sex 、および enum Color はすべて列挙型です。
{} 内の内容は、列挙型の可能な値であり、列挙定数とも呼ばれます。
これらの可能な値はすべて有効で、デフォルトでは 0 から始まり、一度に 1 ずつ増加します。もちろん、定義時に初期値を割り当てることもできます。
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
2. 列挙の利点
なぜ列挙型を使用するのですか。列挙型 enum と構造体のようなものがあります。
列挙が #define で定義された定数のコレクションではないことを理解している友人もいます。では、列挙を作成するとはどういう意味でしょうか?
メリットは何ですか?
列挙型の利点:
1. コードの可読性と保守性が向上します。
2. #define で定義された識別子と比較して、列挙型にはより厳密な型チェックが行われます。
3. 名前汚染の防止 (カプセル化)
4. デバッグが簡単
5. 使いやすく、一度に複数の定数を定義できる
3. 列挙型の使用
列挙の定義、列挙とは何なのか、列挙の利点は何なのかを理解しました。最後に、列挙型の使用方法とその正しい使用方法について学びましょう。
グラフィックプレゼンテーション:
3. ユニオン(コミュニティ)
1. ユニオンの定義
ユニオンは特別なカスタム タイプです
この型で定義される変数も構造体と同様に一連のメンバーを持ち、同じ空間を共有するのが特徴です。
例えば:
実際、union はスペースを一緒に使用することであり、1 つのメンバー変数が変更されると、他のメンバー変数も変更される可能性があり、どちらかが使用され、もう 1 つは適用されない方が適しています。
2. ジョイントの特徴
共用体のメンバーは同じメモリ空間を共有するため、そのようなジョイント変数のサイズは少なくとも最大のメンバーのサイズになります(共用体には少なくとも最大のメンバーを保存する機能が必要であるため)。
それでは、組合員の最初の住所は何でしょうか? 同じですか?
コードデモ:
3. ジョイントサイズの計算
構造体のメモリサイズの計算には多くの規則があることがわかりましたが、計算が少し面倒だと感じる場合は、共用体を使用すると非常に簡単です。点が2つだけ
1. 共用体のサイズは、少なくとも最大のメンバーのサイズとなります。
2. メンバーの最大サイズが最大アライメント数の整数倍ではない場合、最大アライメント数の整数倍にアライメントする必要があります。
最初のケース:
(つまり、最大のものを使用できます。配列や構造体がない場合は、基本的な組み込み型のみです)
このサイズは最大の型のバイト サイズです
2番目のケース:
コードデモ:
union un1 {
char c[5];//5个char类型为5字节
int i;//;两种类型int char 所以最后结果为4 的倍数
//5>4 所以最后为8
};
union un2 {
short c[7];//相当于7个short类型连着 这样是14个字节
int i;//un2 中 类型一共两种 int short 所以int为最大类型,所以最后得到的数据应该是4的倍数
//因为是联合 所以14是这两个联合起来其中最大的一个, 再取4的倍数 ,得到16
};
int main()
{
printf("%d\n", sizeof(union un1));//8
printf("%d\n", sizeof(union un2));//16
return 0;
}
4. 練習問題
現在のコンパイラがエンディアンであることを確認する方法
以前ポインタを学習したときに、最初の方法を学習しました。大小の端[C 言語上級] メモリ内のデータ ストレージの詳細な分析_Xiaowang Xuecode ブログ - CSDN ブログを参照してください。
次に、union? を使ってテストする方法を見てみましょう。
コードは以下のように表示されます:
したがって、VS環境ではリトルエンディアンとなります。
要約する
この章では、構造体の定義、宣言方法、自己参照、構造体変数の定義と初期化、構造体のメモリのアライメント、およびデフォルトのアライメントの変更方法について学びました。次に、列挙の理解と使用法、最後にジョイントの定義特性と問題のサイズの計算方法について説明し、この記事はインタビューでの質問で終わります。
この記事を読んでインスピレーションを得ていただければ幸いです. この段階では列挙と共用体はほとんど使用されませんが, 構造体の内容は非常に重要であることを理解して認識する必要があります. 自己参照 データでは合理的な使用が頻繁に使用されます構造物!!
最後に、あなたのコメント、お気に入り、お気に入りは、Xiao Wang が前進するための原動力です。アドバイスを歓迎します。不足している点があれば、ご指摘ください。ありがとうございます! !!