构造类型
枚举类型
建议:如果定义不相干的常理,使用宏定义(符号常量);如果需要定义一组相关联的常量,如月份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; } -
应用场景:
-
数据类型复杂(结构体、共用体、枚举、结构体指针、无符号的长整型)时使用
-

-
-
为了跨平台的兼容性,例如:
-
size_t:类型重名后的数据类型:
typedef unsigned long size_t; -
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 主要功能
-
初始化牌组:
-
创建包含54张牌的牌组(52张普通牌+2张王牌)
-
普通牌按花色(♠,♥,♣,♦)和点数(3-2)排列
-
-
洗牌功能:
-
使用随机数对牌组进行随机排序
-
确保每次运行洗牌结果不同(基于时间种子)
-
-
发牌功能:
-
将洗好的牌发给3个玩家
-
每个玩家17张牌
-
剩余3张作为底牌
-
-
显示功能:
-
打印每个玩家的手牌
-
打印底牌
-
3. 数据结构
-
suits[]: 存储4种花色符号的字符串数组 -
ranks[]: 存储13个点数等级的字符串数组 -
jokers[]: 存储大小王描述的字符串数组 -
Card结构体: 表示单张牌的数据结构 -
牌组数组:
deck[54] -
玩家手牌数组:
player1[17],player2[17],player3[17] -
底牌数组:
bottomCards[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]);
}
}
-
运行结果

3772

被折叠的 条评论
为什么被折叠?



