C语言:构造类型(枚举)、综合案例:发牌

构造类型

枚举类型

建议:如果定义不相干的常理,使用宏定义(符号常量);如果需要定义一组相关联的常量,如月份0~11,星期0~6,方向0~3,男女0~1等,使用枚举,进行统一管理。以后正式开发中,switch的case后面访问的就是枚举中的常量。

定义

我们一般情况下,定义常量使用宏定义(#define 宏名称 宏值),宏定义非常适合没有关联关系的常量;但是有时候我们可能需要对一组拥有关联关系的量进行定义,如月份0~11,星期0~6,方向0~3等,那么使用宏定义,就不是很清晰,也不方便统一管理,同时还会增加代码量,这个时候就需要用到枚举。

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

说明

枚举定义了一组常量,我们在开发中直接使用这些常量。(常用)

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

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

特点

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

提高了代码的可读性

语法

① 定义枚举类型名以后,就可以定义该枚举类型的变量(先类型,后变量)。(注意:枚举的元素是符号常量)

 enum 枚举类型名 变量列表;

② 在定义枚举类型的同时定义该枚举类型的变量(类型+变量)

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

③ 直接定义枚举变量(变量)

 enum {枚举元素列表} 变量列表;
案例
 #include <stdio.h>
 ​
 void test1()
 {
     // 定义一个枚举类型
     // 注意:枚举类型名一般首字母大写,主要是跟枚举元素名区分
     enum Week
     {
         // 定义枚举元素,元素本质上就是常量,在编译期,会被替换为字面量,枚举元素的命名和符号常量命名一致,都是大写+下换线
         // 多个枚举元素之间使用逗号分隔
         // SUN,MON,TUE,WED,THU,FRI,SAT   // 此时,这7个常量的值依次为:0~6
         SUN = 10,MON,TUE,WED,THU,FRI,SAT   // 此时,这7个常量的值依次为:10~16
     };
     // 1. 直接访问枚举元素,适合于switch
     printf("%d,%d,%d\n", SUN, WED, SAT);   // 10,13,16
     
     // 2. 定义枚举类型的变量,适合于函数传参
     enum Week week;
     // 初始化
     week = TUE; // 不能随便赋值,赋值一定是这个枚举中定义的元素
     printf("%d\n", week); // 12
     
     // 3. 定义枚举类型变量的同时赋值
     enum Week week1 = THU;
     printf("%d\n", week1);// 14
     
     // 4. 可以定义多个枚举变量
     enum THU
     {
         A,B,C   
     } x, y;
     // 赋值
     x = B;
     y = C;
     
     printf("x=%d,y=%d\n", x, y);// 1,2
 }
 ​
 void test2()
 {
     // 定义枚举类型
     enum CaiQuan
     {
         SHI_TOU, JIAN_DAO, BU
     };
     printf("请输入0~2之间的整数:\n0-石头,1-剪刀,2-布\n");
     int choice;
     scanf("%d", &choice);
     
     switch (choice) {
     case SHI_TOU:
         printf("石头\n");
         break;
     case JIAN_DAO:
         printf("剪刀\n");
         break;
     case BU:
         printf("布\n");
         break;
     }
 }
 ​
 int main(int argc, char *argv[])
 {
     test1();
     test2();
     
     return 0;
 }

typedef

  • 说明:给类型重命名,不会影响到类型本身。

  • 作用:给已有的类型起别名。

  • 格式:

     typedef 已有类型名 重命后的类型名;
     // typedef unsigned long size_t;
  • 使用: 

    #include <stdio.h>
     ​
     int main(int argc, char *argv[])
     {
         // 方式1:先定义数据类型,再重命名
         
         // 定义一个结构体
         struct Student
         {
             int id;
             char *name;
             char sex;
             int age;
         };
         
         // 类型重命名
         typedef struct Student Stu;   // 将  struct Student 重命名为Stu
         
         // 使用新类型名
         // 定义结构体实例
         Stu stu = {1,"张三",'w',21};
         printf("%d,%s,%c,%d\n",stu.id, stu.name, stu.sex, stu.age);
         
         Stu *p = &stu;
         printf("%d,%s,%c,%d\n",p->id, p->name, p->sex, p->age);
         
         // 方式2:定义数据类型的同时重命名
         typedef struct PersonInfo
         {
             int a;
             double b;
         } Per;
         
         // 定义变量
         Per per = {2,4.5};
         printf("%d,%.2f\n", per.a, per.b);
         
         // 定义指针
         Per *p1 = &per;
         printf("%d,%.2f\n", p1->a, p1->b);
         
         return 0;
     }

  • 应用场景

    • 数据类型复杂(结构体、共用体、枚举、结构体指针、无符号的长整型)时使用

    • 为了跨平台的兼容性,例如:

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

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

  • 案例:

     #include <stdio.h>
     ​
     struct Student
     {
         int age;
         char *name;
         double scores[3];
     };
     ​
     typedef struct Student Stu_t; // 对类型重命名
     typedef Stu_t* pStu_t;        // 结构体指针重命名
     ​
     void test1()
     {
         Stu_t s1 = {21, "zhangsan",{99,98,97}};
         printf("%d,%s,%.2lf,%.2lf,%.2lf\n",s1.age,s1.name,s1.scores[0],s1.scores[1],s1.scores[2]);
         
         Stu_t *p;
         p = &s1;
         printf("%d,%s,%.2lf,%.2lf,%.2lf\n",(*p).age,p->name,p->scores[0],p->scores[1],p->scores[2]);
     }
     ​
     int main(int argc, char *argv[])
     {
         test1();
         return 0;
     }

综合案例:斗地主

1. 程序概述

这是一个模拟斗地主游戏发牌过程的C语言程序,实现了扑克牌的初始化、洗牌和发牌功能。

2. 功能需求

2.1 扑克牌定义
  • 使用结构体Card表示一张牌,包含:

    • 花色属性suit(0-3表示普通花色♥♠♦♣,4表示小王,5表示大王)

    • 点数属性rank(0-12对应3-A,2,-1表示大小王)

2.2 主要功能
  1. 初始化牌组

    • 创建包含54张牌的牌组(52张普通牌+2张王牌)

    • 普通牌按花色(♠,♥,♣,♦)和点数(3-2)排列

  2. 洗牌功能

    • 使用随机数对牌组进行随机排序

    • 确保每次运行洗牌结果不同(基于时间种子)

  3. 发牌功能

    • 将洗好的牌发给3个玩家

    • 每个玩家17张牌

    • 剩余3张作为底牌

  4. 显示功能

    • 打印每个玩家的手牌

    • 打印底牌

3. 数据结构

  • suits[]: 存储4种花色符号的字符串数组

  • ranks[]: 存储13个点数等级的字符串数组

  • jokers[]: 存储大小王描述的字符串数组

  • Card结构体: 表示单张牌的数据结构

  • 牌组数组: deck[54]

  • 玩家手牌数组: player1[17], player2[17], player3[17]

  • 底牌数组: bottomCards[3]

4. 用户交互

程序运行后自动完成以下流程:

  1. 初始化牌组

  2. 洗牌

  3. 发牌

  4. 显示发牌结果(3个玩家的手牌和底牌)

5. 输出格式

  • 普通牌显示格式:花色+点数(如"♠ 3")

  • 王牌显示格式:"小王"或"大王"

  • 玩家手牌按顺序显示,每张牌用空格分隔

  • 底牌同样格式显示

6. 源码

#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <math.h>
 ​
 #define LEN 54
 ​
 // 定义扑克牌的花色和点数
 const char *suits[] = {"♥", "♠", "♦", "♣"}; // 花色
 const char *ranks[] = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"}; // 点数
 const char *jokers[] = {"小王","大王"}; // 大小王
 ​
 // 定义牌的结构体
 typedef struct
 {
     int suit;  // 花色下标(0~3:普通牌,4:小王,5:大王)
     int rank;  // 点数下标(0~12:普通牌,-1:大小王)
 } Card;
 ​
 // 初始化一副牌
 void initDeck(Card *deck);
 ​
 // 洗牌(打乱牌序)
 void shuffeDeck(Card *deck);
 ​
 // 发牌(3个玩家各自17张牌,最后3张作为底牌)
 void dealCards(Card *deck, Card *player1, Card *player2, Card *player3, Card *bottomCards);
 ​
 // 打印牌,根据数组中提供的花色和点数下标,获取对应字符串进行显示
 void printCard(Card card);
 ​
 int main(int argc, char *argv[])
 {
     // 创建一个数组,用来存放一副牌(54张,包含大小王)
     Card deck[LEN];
     // 创建三个数组,用来存放3个玩家
     Card player1[17], player2[17], player3[17]; 
     // 创建一个数组,用来存放底牌
     Card bottomCards[3];
     
     // 初始化牌
     initDeck(deck);
     
     // 洗牌
     shuffeDeck(deck);
     
     // 发牌(3个玩家各自17张牌,最后3张作为底牌)
     dealCards(deck, player1, player2, player3, bottomCards);
     
     // 打印玩家手牌和底牌
     int i;
     printf("玩家1的手牌:");
     for (i = 0; i < 17; i++) printCard(player1[i]); printf("\n");
     printf("玩家2的手牌:");
     for (i = 0; i < 17; i++) printCard(player2[i]); printf("\n");
     printf("玩家3的手牌:");
     for (i = 0; i < 17; i++) printCard(player3[i]); printf("\n");
     printf("底牌:");
     for (i = 0; i < 3; i++) printCard(bottomCards[i]); printf("\n");
     
     return 0;
 }
 ​
 /**
  * @brief 初始化一副牌
  * 
  * @param deck 一副牌
  */
 void initDeck(Card *deck)
 {
     // 定义一个下标(0~53)
     int index = 0;
     
     // 初始化52张普通牌
     for (int suit = 0; suit < 4; suit++) // 遍历花色
     {
         for (int rank = 0; rank < 13; rank++) // 遍历点数
         {
             // 记录每张牌花色和点数的下标
             deck[index].suit = suit;  // 花色下标
             deck[index].rank = rank;  // 点数下标
             index++;
         }
     }
     
     // 初始化大小王
     deck[index].suit = 4; // 小王
     deck[index].rank = -1;
     index++;
     deck[index].suit = 5;
     deck[index].rank = -1;
 }
 ​
 /**
  * @brief 洗牌(打乱牌序)
  * 
  * @param deck 
  */
 void shuffeDeck(Card *deck)
 {
     // 设置随机种子
     srand((unsigned)time(NULL));
     
     // 洗牌
     // 遍历当前的有序牌
     for (int i = 0; i < LEN; i++)
     {
         // 随机生成0~53之间的数,作为交换牌的下标
         int j = rand() % LEN; // 0~53
         // 交换当前遍历牌跟随机牌的位置
         Card temp = deck[i];
         deck[i] = deck[j];
         deck[j] = temp;    
     }
 }
 ​
 /**
  * @brief 发牌(3个玩家各自17张牌,最后3张作为底牌)
  * 
  * @param deck 
  * @param player1 
  * @param player2 
  * @param player3 
  * @param bottomCards 
  */
 void dealCards(Card *deck, Card *player1, Card *player2, Card *player3, Card *bottomCards)
 {
     // 动态下标
     int index = 0;
     
     // 给玩家发牌
     // for (int i = 0; i < LEN; i++)
     // {
     //      if (i % 3 == 0) // 玩家1
     //      else if(i % 3 == 1) // 玩家2
     //      else if(i % 3 == 2) // 玩家3
     // }
     
     for (int i = 0; i < 17; i++)
     {
         player1[i] = deck[index++]; // 玩家1
         player2[i] = deck[index++]; // 玩家2
         player3[i] = deck[index++]; // 玩家3
     }
     
     // 最后三张作为底牌
     for (int i = 0; i < 3; i++)
     {
         bottomCards[i] = deck[index++];
     }
 }
 ​
 /**
  * @brief 打印牌,根据数组中提供的花色和点数下标,获取对应字符串进行显示
  * 
  * @param card 
  */
 void printCard(Card card)
 {
     if (card.suit == 4 || card.suit == 5)
     {
         // 大小王
         printf("%s ",jokers[card.suit - 4]);
     }
     else
     {
         // 普通牌
         printf("%s %s ",suits[card.suit], ranks[card.rank]);
     }
 }
  • 运行结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值