Day19

结构体类型

结构体数组

案例:

/*
  结构体数组案例:对候选人得票的统计程序。
  设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果
*/
#include <stdio.h>
#include <string.h>

// 定义一个候选人结构体(对象)
struct Person
{
  char *name; // 名字
  int count;  // 票数
};

// 定义候选人数组,并初始化
struct Person persons[3] =
    {
        {"张悦", 0},
        {"李想", 0},
        {"牛逼", 0}};

void main()
{
  int i, j;
  char *leader_name; // 用来接收被投票的候选人姓名

  // 使用一个循环,完成10次投票
  for (i = 0; i < 10; i++)
  {
    printf("请输入您要投票的候选人姓名:\n");
    scanf("%s", leader_name);

    // 给被投票的候选人+1票
    for (j = 0; j < 3; j++)
    {
      // 如何判断两个字符串
      if (strcmp(leader_name, persons[j].name) == 0)
      {
        persons[j].count++;
      }
    }
  }

  printf("\n投票结果:\n");
  for (i = 0; i < 3; i++)
  {
    printf("%5s: %d\n", persons[i].name, persons[i].count);
  }
}

结构体指针

  • 定义:结构体类型的指针变量指向结构体变量或者数组的起始地址。
     
  • 语法:struct 结构体名 *指针变量列表;
     
  • 举例:
    struct Dog
    {
        char name[20];
        int age;
    };
    struct Dog dog;
    struct Dog *p = &dog;

结构体成员访问

  • 结构体数组名访问结构体成员
     
    • 格式:结构体数组名 --> 成员名;
  • 结构体成员访问符

    “ . ” :左侧是结构体变量(结构体对象 / 实例);也可以叫做结构体对象访问成员符;右侧是结构体成员。

    -> :左侧是一个指针,也可以叫结构体指针访问成员符;右侧是结构体成员。
     
  • 访问结构体成员有两种类型,三种方式:
     
    • 类型1:通过结构体对象访问成员
      struct stu
      {
          int id;
          char name[20];
      } stu;
      
      //访问成员
      stu.name;
    • 类型2:通过结构体指针访问成员

           1.指针引用访问成员
      struct stu
      {
          int id;
          char name[20];
      } stu;
      
      struct Stu *p = &stu;
      //指针引用访问成员
      p -> name;

           2.指针解引用间接访问成员

      struct stu
      {
          int id;
          char name[20];
      } stu;
      
      struct Stu *p = &stu;
      //指针解引用间接访问成员
      (*p) -> name;
             3.结构体数组中元素的访问
      struct Stu
      {
          int id;
          char name[20];
          int score;
      } stus[2] = {
          {1,"张三",{86,88,56}},
          {2,"张四",{75,66,78}},
          {3,"王五",{70,99,90}}
      };
      
      //取数据 -- 下标法
      printf("%s,%2f\n",stus[1].name,stus[1].scores[2]);
      
      //结构体成员引用符号: -> 指针法
      printf("%s,%2f\n",stus -> name,stus -> scores[2]); //张三,56
      printf("%s,%2f\n",(stus + 1) -> name,(stus + 1) -> scores[2]); //张四,78
      printf("%s,%2f\n",(*(stus + 1)) -> name,(*(stus + 1)) -> .scores[2]); //王五 90

      小贴士:

      结构体是自定义数据类型,它是数据类型,用法类似于基本类型int;


      结构体数组是存放结构体对象的数组,类似于int数组存放int数据;

      基本类型数组怎么用,结构体数组就怎么用--->可以遍历,可以作为形式参数,也可以做指针等;

  • 结构体类型的使用案例

    代码:
    #include <stdio.h>
    
    //定义结构体
    struct Cat
    {
        char *name;        //姓名
        int age;           //年龄
        char color[20];    //颜色
    };
    
    //1.结构体类型作为形式参数
    void test1(struct Cat c);
    
    //2.结构体类型作为形式参数,结构体类型作为返回值类型
    struct Cat test2(struct Cat c);
    
    //3.结构体数组作为形式参数
    void test3(struct Cat cats[], int len);
    
    //4.结构体数组作为形式参数,结构体指针作为返回值数据类型
    struct Cat *test4(struct Cat cats[], int len);

    测试:

    int main()
    {
      // 定义结构体对象
      struct Cat cat = {"小黑", 8, "baise"};
      // 结构体对象作为实际参数
      test1(cat);
    
      // 定义结构体类型对象
      struct Cat cat1 = {"小白", 8, "heise"};
      // 调用函数并接收返回值
      struct Cat res_cat1 = test2(cat1);
      // 通过返回值访问结构体对象的成员
      printf("%s==%d==%s\n", c1.name, c1.age, c1.color);
    
      // 定义结构体数组
      struct Cat cats[3] = {
          {"汤姆", 16, "蓝色"},
          {"杰瑞", 18, "褐色"},
          {"唐老鸭", 19, "白色"}};
      // 结构体数组名作为实际参数
      test3(cats, 3);
    
      // 定义结构体数组并初始化
      struct Cat cats1[3] = {
          {"汤姆", 16, "蓝色"},
          {"杰瑞", 18, "褐色"},
          {"唐老鸭", 19, "白色"}};
      // 调用函数
      struct Cat *p = test4(cats1, 3);
      struct Cat *w;
      // 通过指针运算遍历数组
      for (w = p; w < p + 3; w++)
      {
        // p[i][j] = *(p[i] + j) = *((p + i) + j)  三者等价
        // 通过结构体指针访问符访问结构体的成员
        printf("%s----%d----%s\n",w -> name,w -> age,w -> color);
      }
    }

    结构体类型求大小

  • 规则:字节对齐(数据在内存中存储在其类型大小的整数倍上)

        1. 首先保证结构体中的成员存储在自身的对齐边界(类型大小的整数倍);

        2. 在满足1的条件下,最终大小要满足 最大成员 所占存储单元的整数倍;

  • 为什么要使用字节对齐:

                节省内存,提高访问效率

  • 在GNU标准中,可以再定义结构体时,

     
  • 柔性数组:
struct st
{
    ...
char a[0];
}

柔性数组不占有结构体的大小。

案例:

/**
 * 求结构体数据类型的大小
 */
#include <stdio.h>
// 定义测试结构体
struct TEST1
{
  char a; // 1
  int b;  // 4
};
struct TEST1_1
{
  char a;                  // 1
  int b;                   // 4
} __attribute__((packed)); // 取消字节对齐,取消之后,结构体数据类型大小就等于其所有成员的数据类型之和
struct TEST1_2
{
  char a __attribute__((aligned(2)));
  int b;
};
struct TEST2
{
  char a;  // 1
  short c; // 2
  int b;   // 4
};
struct TEST3
{
  int num;       // 4
  char name[10]; // 10
  char sex;      // 1
  int age;       // 4
  double score;  // 8
};
struct TEST4
{
  int num;       // 4
  short name[5]; // 10
  char sex;      // 1
  int age;       // 4
  int scores[2]; // 8
};
int main()
{
  // 创建结构体变量
  struct TEST1 test1;
  struct TEST2 test2;
  struct TEST3 test3;
  struct TEST4 test4;
  struct TEST1_1 test1_1;
  struct TEST1_2 test1_2;
  // 计算大小
  printf("%lu\n", sizeof(test1));
  printf("%lu\n", sizeof(test2));
  printf("%lu\n", sizeof(test3));
  printf("%lu\n", sizeof(test4));
  printf("%lu\n", sizeof(test1_1));
  printf("%lu\n", sizeof(test1_2));
}

推导过程:快速计算结构体大小:
 

https://blog.csdn.net/weixin_72357342/article/details/131135555
https://blog.csdn.net/x2528238270/article/details/120798606

共用体 / 联合体

  • 定义:使几个不同的变量占用同一段内存的结构。共用体按定义中需要存储空间最大的成员来分配存储单元,其他成员也是用该空间,他们的首地址是相同的。
     
  • 定义格式
    union 共用体名称
    {
        数据类型 变量名;
        数据类型 变量名;
        ...
    };
  • 共用体的定义和结构体类型相似:

         1. 可以有名字,也可以匿名;

         2. 共用体在定义时也可以定义共用体变量;

         3. 共用体在定义时也可以初始化成员;

         4. 共用体也可以作为形参和返回值类型使用:

         5. 共用体也可以定义共用体数组

            ...

    也就是说,结构体,共用体都支持。
     
  • 注意:
     
    • 共用体弊大于利,尽量少用,一般很少用;
       
    • 共用体变量在某一时刻只能存一个数据,并且也只能取出一个数。
       
    • 共用体和结构体都是自定义数据类型,用法类似于基本数据类型
      • 共用体可以是共用体的成员,也可以是结构体的成员。
      • 结构体可以使共用体的成员,也可以是共用体的成员。
    • 案例:
      /**
       * 共用体
       */
      #include <stdio.h>
      // 定义共用体
      union S
      {
        char a;
        float b;
        int c;
      };
      // 共用体作为共用体的成员
      union F
      {
        char a;
        union S s;
      };
      // 共用体作为结构体的成员
      struct G
      {
        int a;
        union S s;
      };
      // 定义一个结构体
      struct H
      {
        int a;
        char b;
      };
      // 结构体作为结构体成员
      struct I
      {
        int a;
        int b;
        struct H h;
      };
      // 共用体作为结构体成员
      struct J
      {
        int a;
        char b;
        union S s;
      };
      void test1()
      {
        // 定义共用体类型
        union Stu
        {
          int num;
          char sex;
          double score;
        };
        // 定义匿名共用体:匿名共用体一般作为结构体成员或者其他共用体成员
        union
        {
          int a;
          char c;
        } c;
        printf("%lu,%lu\n", sizeof(union Stu), sizeof(c));
      }
      void test2()
      {
        union C
        {
          int a;
          char b;
        };
        // 定义变量
        union C c;
        // 存数据
        c.a = 10;
        c.b = 'A';
        printf("%d---%d\n", c.a, c.b); // 取数据
        c.a += 5;
        printf("%d---%d\n", c.a, c.b); // 取数据
        union E
        {
          char *f;
          long a;
          int b;
        } e = {"hello world!"};
        printf("%s,%p---%ld,%p---%d\n", e.f, &(e.f), e.a, &(e.a), e.b);
      }
      int main()
      {
        test1();
        test2();
      }

枚举

  • 定义:

            我们一般情况下,定义常量使用宏定义(#define宏名称 值),宏定义非常适合没有关联关系的常量;但是有时候我们可能需要对一组拥有关联关系的量进行定义,比如 周一~周日1月~12月 等,那么使用宏定义,就不是很清晰,在这个时候就需要使用到枚举。

            枚举的存在就是将多个拥有关联关系的常量组合到一起,提高代码的可读性。
     
  • 说明:

         枚举类型定义了一组常量,我们在卡法中直接使用这些常量(常用)

         当然枚举类型也可以类似于结构体一样定义变量等操作。(不常用)

         枚举常量有默认值,从0开始依次加1;我们可以在定义时指定它的值,如果个别没有赋值,可以根据赋值依次加1推导。
     
  • 特点:

    定义了一组常量,类似于定义了多个自定义常量(宏定义)

    提供了代码的可读性(避免了魔术数字)
     
  • 定义语法:

    定义枚举类型名以后就可以定义该枚举类型的变量
    enum 枚举类型名 变量列表;

    在定义枚举类型的同时定义该枚举类型的变量。

    enum 枚举类型名{ 枚举元素列表 }变量表;

    直接定义枚举类型变量。

    enum { 枚举元素列表 }变量表;
  • 案例:
    /*
      枚举类型
    */
    #include <stdio.h>
    
    // 常量之宏定义
    // 常量的命名:大写英文字母+下划线,举例:MAX_VALUE
    #define PI 3.1415926
    
    void test1()
    {
      // 定义枚举类型
      enum Week
      {
        SUN = 10,
        MON,
        TUE,
        WED,
        THU,
        FRI,
        SAT
      };
    
      printf("%d,%d,%d\n", SUN, WED, SAT);
    
      // 定义枚举类型的变量(先定义变量,后赋值)
      enum Week w;
      // 初始化
      w = MON;
      printf("%d\n", w);
    
      // 定义枚举类型的变量同时赋值(定义变量的同时赋值)
      enum Week w1 = THU;
      printf("%d\n", w1);
    
      enum H
      {
        A,
        B,
        C
      } x,
          y;
    
      x = B;
      y = C;
      printf("x=%d,y=%d\n", x, y);
    }
    
    void test2()
    {
      // 定义枚举
      enum CaiQuan
      {
        SHI_TOU,
        JIAN_DAO,
        BU
      };
      printf("请输入0~2之间的整数: [0-石头,1-剪刀,2-布]\n");
      int number;
      scanf("%d", &number);
    
      switch (number) // switch 和 enum 是天生的搭档
      {
      case SHI_TOU:
        printf("石头\n");
        break;
      case JIAN_DAO:
        printf("剪刀\n");
        break;
      case BU:
        printf("布\n");
        break;
      }
    }
    
    int main()
    {
      test1();
      test2();
    }

    typedef

  • 说明:给类型重命名,不会影响到类型本身
     
  • 作用:给已有的类型起别名
     
  • 格式:
    typedef 已有类型名 新别名;

使用:

//定义结构体
struct Stu
{
    int a;
    char *name;
    char sex;
    int age;
};

//类型重命名
typedef struct Student Stu;

//定义变量
struct Stu stu = {1,"张甜",'M',21};

//定义结构体的同时类型重命名
typedef struct PersonInfo
{
    int a;
    double b;
} Per;

//定义变量
struct Per per = {2,5};
  • 应用场景

            1.数据类型复杂(结构体,共用体,枚举,结构体指针)时使用

            2.为了跨平台兼容性,例如:

                    1.size_t : 类型重命名后的数据类型,typedef unsigned long size_t;

                        2.unit_16 : 类型重命名后数据类型

  • 案例
    // 类型重命名
    #include <stdio.h>
    struct Student
    {
      int age;
      char *name;
      double score;
      int arr[3];
    };
    typedef struct Student Stu_t;
    typedef Stu_t *pStu_t;
    void test1()
    {
      Stu_t s1 = {23, "zhangsan", 23.33, {11, 22, 33}};
      printf("%d, %s, %f, %d\n", s1.age, s1.name, s1.score, s1.arr[0]);
      // Stu_t *p = &s1;
      Stu_t *p;
      p = &s1;
      pStu_t p2;
      p2 = p;
      printf("%d, %s, %f, %d\n", p2->age, p2->name, p2->score, p2->arr[0]);
    }
    int main()
    {
      test1();
      return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值