1、共用体(Union)是一种特殊的数据类型,也被称为联合体,它允许在相同的内存位置存储不同的数据类型,每次只能存储其中一种类型的值。共用体是一种数据结构,多个不同类型的变量能够共享同一段内存空间。在C语言中,共用体通过关键字union定义。
主要用途
节省内存:当程序中需要存储多种类型的数据,但同一时间只使用其中一种类型时,可以使用共用体来节省内存空间。因为共用体的总长度等于其成员中最长数据类型的长度,而不是所有成员长度之和。
数据转换:共用体可以用于不同类型数据之间的快速转换,例如,可以将一个整数存储在一个共用体中,然后将其解释为浮点数或字符数组等。
特定场景下的高效操作:在需要处理多种数据类型但每次只处理一种的场景下,如硬件编程、网络通信协议解析等,共用体可以提供高效的数据处理方式。
特别之处
共享内存:共用体的所有成员共享同一段内存空间,在同一时刻,共用体只能存储其成员中的一种类型的数据。
长度特性:共用体的长度等于其成员中最长数据类型的长度。
赋值特性:对共用体中的任一成员赋值都会覆盖该内存位置上的旧值,因此共用体变量中起作用的成员是最后一次被赋值的成员。
定义与声明
共用体的定义和声明方式如下:
union 共用体名 {
类型1 成员名1;
类型2 成员名2;
...
类型N 成员名N;
};
// 声明共用体变量
union 共用体名 变量名;
测试代码:
#include "date.h"
#include <stdio.h>
// 定义共用体
union MyUnion {
char c;
short s;
int i;
long long ll;
};
int main() {
int time = getTime();
union MyUnion myUnion;
// 初始化共用体的 long long 成员
myUnion.ll = 0x123456789ABCDEF0LL;
// 打印 long long 成员的值,验证初始化
printf("long long ll: %lld (0x%llx)\n", myUnion.ll, myUnion.ll);
// 检查其他成员的值,它们共享同一段内存。
// 这些打印的值将取决于我们如何解释这段内存(即,作为 char、short、int 还是 long long)
// 打印 char 成员的值(只取最低字节)。
printf("char c: %c (0x%x)\n", myUnion.c, (unsigned char)myUnion.c);
// 打印 short 成员的值(取最低两个字节,注意字节序)。
// 假设系统是小端字节序
printf("short s: %d (0x%x)\n", myUnion.s, (unsigned short)myUnion.s);
// 打印 int 成员的值(取最低四个字节,注意字节序)。
// 假设系统是小端字节序 。
printf("int i: %d (0x%x)\n", myUnion.i, (unsigned int)myUnion.i);
// 内存布局,打印出每个成员的地址。
// 这些地址将是相同的(对于某些编译器和平台,偏移量将是内存对齐的倍数)。
printf("Addresses:\n");
printf("&myUnion.ll: %p\n", &myUnion.ll);
printf("&myUnion.c: %p\n", &myUnion.c);
printf("&myUnion.s: %p\n", &myUnion.s);
printf("&myUnion.i: %p\n", &myUnion.i);
// 由于是共用体的成员,所以它们的地址在逻辑上是相同的。
// 编译器会根据类型给出不同的指针类型(比如 char*、short*、int*、long long*)。
return 0;
}
运行结果如下:
...........................................................................................................................................................
2、枚举(Enumeration)是用户定义的类型,允许程序员为整数指定一个更容易理解的名字。枚举类型是一组命名的整型常量,在逻辑上相关,通常用于表示状态、选项或其他类似的集合。
枚举类型的定义使用enum
关键字,后跟枚举类型的名称和用花括号括起来的一组枚举常量。每个枚举常量都是一个整数,默认情况下,从0开始,每个后续常量递增1。也可以显式地指定某个枚举常量的值。
测试代码1:
#include "date.h"
#include <stdio.h>
// 定义一个枚举。
// 枚举成员的默认值是从0开始的整数序列,也可以在枚举定义中显式指定它们。
enum days {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
int main() {
int time = getTime();
enum days today;
// 使用枚举成员直接赋值。
today = Tuesday;
printf("Today is day %d of the week.\n", today);
// 直接将int赋给枚举变量,建议使用显式转换让代码更清晰。
int dayNumber = 3; // 3代表Wednesday
today = (enum days)dayNumber; // 显式转换
printf("This day with value %d is %d.\n", dayNumber, today);
// 直接使用枚举成员。
printf("Thursday is %d.\n", Thursday);
return 0;
}
运行结果如下:
测试代码2:
#include "date.h"
#include <stdio.h>
// 定义一个枚举
enum days {
Sunday = 1, // 显式指定Sunday的值为1
Monday = 2,
Tuesday = 3,
Wednesday = 4,
Thursday = 5,
Friday = 6,
Saturday = 7
};
int main() {
int time = getTime();
enum days today = Wednesday; // today的值为4
// 打印today的值
printf("Today is day %d of the week.\n", today);
// 使用显式指定的值
enum days specialDay = (enum days)6;
printf("Special day is %d.\n", specialDay);
// 使用枚举成员直接比较
if (specialDay == Wednesday) {
printf("Yes, it's Wednesday!\n");
} else{
printf("It's not Wednesday.\n");
}
return 0;
}
运行结果如下:
测试代码3:
#include "date.h"
#include <stdio.h>
#include <string.h>
// 定义枚举类型 JobType,包含STUDENT和TEACHER两个值。
typedef enum { STUDENT, TEACHER } JobType;
// 定义结构体 Person,包含学号、姓名、工作类型、工作类别联合体,以及指向下一个Person结构的指针。
struct Person {
int num;
char name[20];
JobType job;
union {
int clas;
char position[11]; // +1 for null terminator 。
} category; // union 是结构体 Person 的一个成员 。
struct Person* next;
};
// 定义初始化学生信息的函数 initStudent,设置学号、姓名、工作类型和班级信息,以及将下一个指针置为空。
void initStudent(struct Person* person, int num, const char* name, int clas) {
person->num = num;
strcpy(person->name, name);
person->job = STUDENT;
person->category.clas = clas;
person->next = NULL;
}
// 定义初始化老师信息的函数 initTeacher,设置工号、姓名、工作类型和职位信息,以及将下一个指针置为空。
void initTeacher(struct Person* person, int num, const char* name, const char* position) {
person->num = num;
strcpy(person->name, name);
person->job = TEACHER;
strcpy(person->category.position, position);
person->category.position[10] = '\0';
person->next = NULL;
}
int main() {
int time = getTime();
// 创建名为student和teacher的结构体实例。
struct Person student, teacher;
// 调用initStudent和initTeacher函数初始化。
initStudent(&student, 1, "Alice", 3);
initTeacher(&teacher, 2, "Bob", "Professor");
// 使用条件语句检查学生和老师的工作类型,并打印相应的信息。
if (student.job == STUDENT) {
printf("Student #%d, Name: %s, Class: %d\n", student.num, student.name, student.category.clas);
}
if (teacher.job == TEACHER) {
printf("Teacher #%d, Name: %s, Position: %s\n", teacher.num, teacher.name, teacher.category.position);
}
return 0;
}
运行结果如下:
测试代码4:
#include "date.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 定义一个枚举类型Suit,表示扑克牌的花色
typedef enum { CLUBS, DIAMONDS, HEARTS, SPADES } Suit;
// 定义一个结构体Card,表示一张扑克牌,包括rank(点数)和suit(花色)两个成员变量。
typedef struct {
int rank;
Suit suit;
} Card;
// shuffle函数,对一副牌进行洗牌操作,通过随机交换牌的位置洗牌。
void shuffle(Card deck[], int n) {
srand(time(0));
for (int i = 0; i < n; i++) {
int j = rand() % n;
Card temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
}
// printCard函数,打印一张扑克牌的点数和花色。
void printCard(Card card) {
switch (card.rank) {
case 1:
printf("Ace");
break;
case 11:
printf("Jack");
break;
case 12:
printf("Queen");
break;
case 13:
printf("King");
break;
default:
printf("%d", card.rank);
}
switch (card.suit) {
case CLUBS:
printf(" of Clubs\n");
break;
case DIAMONDS:
printf(" of Diamonds\n");
break;
case HEARTS:
printf(" of Hearts\n");
break;
case SPADES:
printf(" of Spades\n");
break;
}
}
int main() {
int time = getTime();
// 创建一个包含52张扑克牌的数组deck
const int NUM_CARDS = 52;
Card deck[NUM_CARDS];
// for循环初始化每一张牌的点数和花色。
for (int i = 0; i < NUM_CARDS; i++) {
deck[i].rank = (i % 13) + 1;
deck[i].suit = static_cast<Suit>(i / 13); //显式类型转换。
// 打印洗牌前的点数和花色。
printCard(deck[i]);
}
printf("\n");
// 调用shuffle函数进行洗牌
shuffle(deck, NUM_CARDS);
// 循环打印洗牌后每张牌的点数和花色。
for (int i = 0; i < NUM_CARDS; i++) {
printf("Card %d: ", i + 1);
printCard(deck[i]);
}
return 0;
}
运行结果如下: