C言語集錦|03-C言語の複合データ型(typedefキーワード、構造体、列挙体、共用体)


ドキュメントのバージョン
更新時間
コンテンツの更新
v1.0
2020-09-14
初稿が完成する
文書ディレクトリ
  • 一、typedefキーワード
  • 二、構造体(重点)
  • 1. なぜ構造体
  • が必要なのか
  • 2. 構造体
  • とは
  • 3. 構造体
  • の定義方法
  • 4. 構造体
  • の使用方法
  • 4.1. 付与および初期化
  • 4.2. 各メンバー
  • へのアクセス
  • 4.3. 構造体変数の演算
  • 4.4. 構造体を関数として伝達する問題
  • 5. 構造体メモリの位置合わせの問題(面接試験)
  • 5.1. 問題の説明
  • 5.2. 問題の原因
  • 5.3. 問題解析
  • 6. 構造体配列
  • 6.1. 静的配列
  • 6.2. 動的配列

  • 三、列挙体
  • 1. 列挙体
  • とは
  • 2. 列挙体
  • の定義方法
  • 3. 列挙体
  • の使用方法
  • 4.コモン
  • 1. 共用体
  • とは
  • 2. なぜ共用体
  • が必要なのか
  • 3. コモン
  • を定義する方法
  • 4. コモン
  • の使用方法

    一、typedefキーワード
    typedefは、データ型に別名を付けるために使用されます.次のように使用されます.
    typedef <      > <   >;
    

    例:
    typedef unsingned char uint8_t;
    typedef unsingned int  uint16_t;
    

    二、構造体(重点)
    1.なぜ構造体が必要なのか
    複雑なものを表すために、通常のデータ型は要求を満たすことができません.
    2.構造体とは
    いくつかの基本データ型を組み合わせて形成された新しいデータ型を構造体と呼ぶ.
    3.構造体の定義方法
    構造体を定義するには、次の4つの方法があります.
    ①データ型のみを定義し、変数は定義しない:
    struct student_st {
         
    	char name[20];	/*      */
    	char sex;		/*      */
    	float score;	/*      */
    };
    

    ②データ型を定義し、変数を定義します.
    struct student_st {
         
    	char name[20];	/*      */
    	char sex;		/*      */
    	float score;	/*      */
    } st1;
    

    ③データ型を定義し、変数を定義しますが、構造体型は匿名です.
    struct {
         
    	char name[20];	/*      */
    	char sex;		/*      */
    	float score;	/*      */
    } st1;
    

    ④データ型を定義し、変数を定義せず、同時に新しいタイプに別名を付ける(多用):
    typedef struct student_st {
         
    	char name[20];	/*      */
    	char sex;		/*      */
    	float score;	/*      */
    } student_t;
    

    4.構造体の使い方
    4.1. 割り当てと初期化
    構造体変数には、定義と同時に初期値のみが付与されます.
    student_t st1 = {
         "mculover666", 'M', 66.6};
    

    定義が完了すると、全体的に値を割り当てることはできません.単一の値しか与えられません.
    4.2. 各メンバーへのアクセス
    構造体のメンバーにアクセスするには、次の2つの方法があります.
  • は、構造体変数を介してメンバーにアクセスする.
  • は、構造体変数を指すポインタによってメンバーにアクセスする.

  • ①構造体変数によるアクセス:
    printf("name:%s sex:%c score:%.1f\r
    "
    , st1.name, st1.sex, st1.score);

    ②構造体変数へのポインタでアクセス:
    student_t* st_ptr = &st1;
    printf("name:%s sex:%c score:%.1f\r
    "
    , st_ptr->name, st_ptr->sex, st_ptr->score);

    4.3. 構造体変数の演算
    構造体変数間では加算、減算、相互乗算はできませんが、構造体変数間では相互に値を割り当てることができます.
    student_t st1 = {
         "mculover666", 'M', 66.6};
    student_t st2;
    st2 = st1;
    printf("name:%s sex:%c score:%.1f\r
    "
    , st2.name, st2.sex, st2.score);

    4.4. 構造体が関数として問題を伝達する
    構造体を関数パラメータとして渡す場合は、次の2つの方法があります.
  • 直接構造体変数を伝達する:スタックのオーバーヘッドが大きく、関数では構造体の修正ができない.
  • 構造体ポインタを直接渡します.4バイト(または8バイト)のポインタだけで、関数で構造体を変更できます.
  • 5.構造体メモリの位置合わせの問題(面接試験)
    5.1. 問題の説明
    次のコードを実行します.
    printf("%d + %d + %d = %d", sizeof(st1.name), sizeof(st1.sex), sizeof(st1.score), sizeof(st1));
    

    実行結果:
    20 + 1 + 4 = 28
    

    不思議なことに、構造体は25バイトを占有すべきではありませんか?なぜ28バイトなの?
    st 1の各メンバーのアドレスを印刷するとわかります.
    printf("st1:      %p
    st1.name: %p
    st1.sex: %p
    st1.score:%p
    "
    , &st1, &st1.name, &st1.sex, &st1.score);

    印刷結果:
    st1:      000000000061FDF0
    st1.name: 000000000061FDF0
    st1.sex:  000000000061FE04
    st1.score:000000000061FE08
    

    sex変数はcharタイプで、もともと1バイトを占有すべきだったのに、実際には4バイトを占有していたことがわかります.
    5.2. 問題の原因
    コンパイラは、構造体の変数にスペースを割り当てるときにメモリ整列規則を使用します.
    1構造体変数の開始アドレスは、その最も広いメンバーサイズで除去することができる.
    ②構造体の各メンバーの先頭アドレスに対するオフセットは、自身のサイズで割り切れるが、できない場合は、前のメンバーの後に空白バイトを補充する.
    ③構造体の全体的なサイズは、最も広いメンバーのサイズで除算できます.そうしないと、空白バイトが後で補充されます.
    5.3. もんだいぶんせき
    2番目のメンバsexはcharタイプであり、1バイトしか占有しないため、後のメンバscore定義の場合、自身のサイズは4であり、その結果、000000000061FE04+1は整列規則を満たすことができないことが分かったので、空白バイトを補充し、前に20+4=24バイトがあるようにし、アドレスを000000000061FE08に変更した.
    これらの空白バイトを手動で補充すると、構造体全体のサイズは28バイトになります.
    typedef struct student_st {
         
        char name[20];	/*      */
        char sex;		/*      */
        char reserve_bytes[3];  /*      */
        float score;	/*      */
    } student_t;
    

    6.構造体配列
    6.1. 静的配列
    student_t st[100];
    

    6.2. ダイナミック配列
    #define STUDENT_NUM 100
    
    /*        */
    student_t* st_ptr = (student_t *)malloc(STUDENT_NUM * sizeof(student_t));
    
    /*   ... */
    
    /*          */
    free(st_ptr);
    st_ptr = NULL;
    

    三、列挙体
    1.列挙体とは
    列挙体は、1つのタイプの変数のすべての可能な値を羅列し、このタイプの変数に他の値を割り当てることはできません.
    2.列挙体の定義方法
    上記構造体の1つのメンバーはsex(性別)であり、列挙体を使用するのに適している.
    列挙体を定義する方法は2つあります.
    ①列挙タイプのみ定義:
    enum student_sex_en {
         
    	MALE = 'M',
    	FAMALE = 'F',
    };
    

    ②列挙タイプを定義するとともに、新しい名前を付けて使いやすい.
    typedef enum student_sex_en {
         
    	MALE = 'M',
    	FAMALE = 'F',
    } student_sex_t;
    

    3.列挙体の使用方法
    まず、以前に定義した構造体を最適化します.
    typedef struct student_st {
         
        char name[20];	/*      */
        student_sex_t sex;		/*      */
        char reserve_bytes[3];  /*      */
        float score;	/*      */
    } student_t;
    

    次に、sex変数に値を割り当てるときに使用します.
    student_t st1 = {
         "mculover666", MALE, 66.6};
    

    四.きょうようたい
    1.共用体とは
    共通体とは、複数の変数が同じメモリ空間を共有し、共通体の大きさは最大変数が占有するメモリ空間である.
    2.なぜ共用体が必要なのか
    最も典型的な使い方の一つは、構造体が差別化されていないことです.
    たとえば、構造体があります.
    typedef struct num_st {
         
        int a;
        int b;
        int c;
        int d;
    } num_t;
    

    使用状況は次のとおりです.
  • 賦値時には、1つのサイクルで賦値を遍歴することが要求され、メンバーの名前の違いを無視し、これを無差異化遍歴と呼ぶ.
  • 使用時にメンバー名に従ってアクセスできることが要求される.

  • 明らかに、最初の需要は配列を使用するのに適しており、2番目の需要は構造体を使用するのに適しているので、共通体を使用します.配列と構造体に同じメモリ空間を共有させます.
    3.共通体の定義方法
    上記の要件に従って共通体を定義し、新しい名前を付けます.この共通体が占有する空間は4つのintの大きさです.
    typedef union num_un {
         
        num_t num;
        int   n[4];
    } num_u;
    

    4.共通体の使い方
    値を割り当てるときに配列に従って使用し、最初の要件を満たすために直接ループします.
        num_u num1;
    
        for (int i = 0; i < 4; i++) {
         
            num1.n[i] = i;
        }
    

    データにアクセスするときは、構造体に従って使用し、名前に従って値を取り、2番目の要件を満たします.
    printf("a = %d, b = %d, c = %d, d = %d\r
    "
    , num1.num.a, num1.num.b, num1.num.c, num1.num.d);

    最終的な実行結果は次のとおりです.
    a = 0, b = 1, c = 2, d = 3