目录
前言
结构体是一种用户自定义的数据类型,可以包含多个不同类型的数据。结构体的成员可以是基本数据类型,也可以是其他结构体。联合体也是一种用户自定义的数据类型,但它可以存储多个不同类型的数据,但在任何时候只能存储其中一个成员的值。联合体的成员必须是相同的数据类型。下面我们来一一介绍他们的使用和区别。
一、结构体变量
不变的数据用常量来处理,会变化的数据用变量来处理,同类型多个数据用数组来处理。具有多个不同类型的数据的集合用什么数据结构来处理呢?答案是用结构体。
结构体就是把具有内在联系的多个不同类型的数据结合成一个整体,使它们关联起来。
1.1 结构体变量的定义形式
1)原始形式
结构体类型定义的初级形式:
struct
{
类型名1 成员名 1;
类型名2 成员名 2;
......
类型名n 成员名 n;
} 结构体变量名 = {初始化元素1,初始化元素2};
代码示例:
#include <stdio.h>
int main() {
struct {
int age;
int score;
char Name[20];
}People;
//1 使用结构体成员,通过"."符号
//2 使用的时候,就和普通变量一样
//2.1 赋值
People.age = 30;
People.score = 90;
strcpy_s(People.Name, 20, "xiaohong");
//2.2 可以使用 scanf_s 进行键盘输入
printf("请输入年龄:");
scanf_s("%d", &People.age);
//2.3 可以使用 printf 打印
printf("%d\n", People.age);
printf("%d\n", People.score);
printf("%s\n", People.Name);
return 0;
}
2)简便形式
以上这种定义结构体变量的方式,非常的麻烦。假如定义结构体变量都像之前这样每次都写那么多,一定会累吐血,我们可以先构造出类型,然后使用类型名定义变量。
所以便出现了简便方式(这种方式是最为常用的):
struct 结构体名
{
类型名1 成员名1;
.....
类型名n成员名n;
};
这么写并非为了定义一个结构体变量,而是产生了一个结构体类型,如果需要,我们可以用这个类型定义无数的变量。
例如:
1. 定义结构体类型定义
struct _PERSON {
char name[20];
char sex;
int age;
float height;
}
2. 定义结构体变量
struct _PERSON abc;
1) 表示定义了一个结构体类型 struct person,它由 name、sex、age 和height 四个成员组成。
2) 表示定义一个结构体类型的变量
例如:
int main()
{
//构造一个结构体类型
struct _PEOPLE{
int age;
int score;
char Name[20];
};
//定义一个结构体变量,初始化结构体变量,放在括号{}中
_PEOPLE People1 = {18, 100, "xiaohong"};
//使用结构体变量
People1.age = 123;
People1.score = 100;
strcpy_s(People1.Name, 20, "xiaoming");
return 0;
}
3)中间形式
我们也可以在给结构体起名的同时定义一个变量,这不常用。
struct _PERSON {
char name[20];
char sex;
int age;
float height;
}per;
per就是_PERSON类型的变量。
1.2 结构体变量的初始化
与普通变量一样,结构体类型的变量也可以在定义时进行初始化。将结构体变量各成员
的初值顺序地放在一对大括号中,并用逗号分隔。
struct person {
char name[20];
char sex;
int age;
float height;
}per = {"xiaoming", 'M', 18, 185.5};
对结构体类型变量赋初值时,按每个成员在结构体中的顺序一一对应赋值。
1.3 结构体变量的引用
结构体变量的引用必须在定义结构体变量后进行,对结构体变量的引用可以分为对结构体变量中成员的引用和对整个结构体变量的引用。引用结构体成员的一般形式为:
结构体变量名.成员名
“.”是成员运算符。它在所有运算符中优先级最高如果某个成员本身又是结构体变量,则必须连续使用成员运算符,直到最低一级成员才能进行运算,结构体变量的每个成员都属于某种数据类型,因此都可以像普通变量一样进行其类型允许的各种操作。上述代码均有示例。
二、结构体的嵌套
C语言中的结构体不能直接嵌套自己本身,以避免无限递归和内存溢出的问题。但是,结构体可以嵌套指向自己类型的指针。下面是一个简单的代码示例来说明这个概念:
#include <stdio.h>
#include <stdlib.h>
// 定义一个结构体,包含一个整型数据成员和一个指向相同类型的指针
typedef struct Node {
int data;
struct Node* next;
} Node;
int main() {
// 创建一个指向Node类型的指针
Node* head = (Node*)malloc(sizeof(Node));
// 为该结构体分配内存空间
if (head == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 初始化结构体成员
head->data = 10;
head->next = NULL;
// 可以添加更多的Node结构体到链表中...
// 在完成链表操作后,别忘了释放内存
free(head);
return 0;
}
这个代码示例展示了如何定义一个包含整型数据成员和一个指向相同类型的指针的结构体。注意,我们使用malloc来动态地为结构体分配内存空间,并且在操作完成后使用free释放内存空间。这是因为在C语言中,动态分配的内存空间需要手动管理。
三、结构体数组
定义结构体数组的方法和定义结构体变量的方法一样,定义结构体变量的三种方式都可以用来定义结构体数组,例如:
定义结构体的时候定义数组,但是这种方式不常用:
struct person
{
char name[20];
char sex;
int age;
float height;
}per[3];
定义结构体之后,使用结构体名定义,这种方式是最常见的,如下所示。
struct person
{
char name[20];
char sex;
int age;
float height;
};
struct person per[3];
数组每个元素的初值都放在一对大括号中,括号中依次排列元素各成员的初始值。与一般数组的初始化一样,如果给出了全部元素的初值,则数组的长度可以不指定,由系统根据初值的数目来确定数组长度。
struct person
{
char name[20];
char sex;
int age;
float height;
}per[3] = { {"LiHong", 'M', 20, 175},
{"WangTian",'F', 19, 168.5},
{"Zhao Min", 'M', 23, 186}};
结构体数组,在内存中也是连续的。使用方式是数组和结构体使用方式的组合。引用只要遵循对数组元素的引用规则和对结构体变量成员的引用规则即可,例如:
#include <stdio.h>
#include <string.h>
int main()
{
//构造一个结构体类型
struct PEOPLE {
int age;
int score;
char Name[20];
};
//定义一个结构体变量,初始化结构体变量,放在括号{}中
PEOPLE People = {18, 99, "xiaoming"};
//使用结构体变量
People.age = 23;
People.score = 100;
strcpy_s(People.Name, 16, "xiaohong");
return 0;
}
注意:在 C 语言编译器中,定义结构体时,struct person 前面的 struct 是必须的,但是在 C++编译器中,这个不是必须的。
四、联合体类型
4.1 联合体的基本定义
联合体有时也被称之为共用体,其基本使用语法,和结构体是一模一样的。(只有关键字是 union)。
定义形式如下:
union 联合体名
{
类型名1 成员名 1;
类型名2 成员名 2;
... ...;
类型名n 成员名 n;
};
例如:
union _UNIONNAME
{
int a;
int b;
};
使用方式等全部和结构体一样。
4.2 联合体和结构体的区别
联合体和结构体有什么区别?等下回答一下这个问题,请看结论:
1)结构体,每一个成员单独占用内存空间
2)联合体,所有的成员共享一块空间
通过下面的实验,我们通过查看结构体内存情况,输出结构体成员的方式来验证上面的结论。
#include <stdio.h>
#include <string.h>
struct _TEST
{
int a;
int b;
};
int main()
{
_TEST test;
test.a = 10;
test.b = 20;
printf("%d\n", test.a);
printf("%d\n", test.b);
return 0;
}
编译运行程序后如下断点调试
在监视器窗口中输入test.a和test.b的取地址
然后在内存窗口中输入test.a的地址搜索 ,对应的16进制数据是0a,test.b的是14。
可以看到,在内存中,test的a 与b 是占用了不同的空间的。
下面是联合体的示例:
#include <stdio.h>
#include <string.h>
union _TEST
{
int a;
int b;
};
int main() {
_TEST test;
test.a = 10;
test.b = 20;
printf("%d\n", test.a);
printf("%d\n", test.b);
return 0;
}
编译运行后如下断点调试
在监视窗口输入如下值,可以看到test.a和test.b的值都是20。
然后根据地址值0x000000e04176f9b0在内存窗口搜索查看其内存值16进制为14。
可以看到,在内存中 test的 a 与 b 是占用了相同的空间,都是一样的值。
4.3 联合体的应用
联合体具有以上的特性,那么我们用它来干么呢?
通常来说有两种用途:
1. 同一个数据需要多种表现形式
2. 在互斥情况之下的节省空间
例如:
union IP
{
unsigned int Num;
struct _IPF
{
unsigned char ip1;
unsigned char ip2;
unsigned char ip3;
unsigned char ip4;
};
};
这种情况,就是同一个数据的多种表现形式,IP 地址我们知道是例如:192.168.1.1这样的四个数字,本质上来说,IP 是四个字节的整数,但是很难记忆,所以使用习惯上,我们把他每个字节单独分出来读取,记忆符合我们人类的习惯,但是计算机还是使用4字节整数的,所以对于 IP 来说,就有两种表现形式,可以使用以上的联合体形式实现。再例如:
struct _Bachelor
{
int nGaokaoScore;
int yuwenScore;
//....
};
struct _Master
{
int nKaoyanScore;
//..
};
struct _Doctor
{
int nlunwenScore;
//..
};
struct _STUDENT
{
int nlevel; //本科,研究生,博士 0,1,2
char Name[20];
union
{
_Bachelor benke;
_Master yanjiusheng;
_Doctor boshisheng;
}scores;
};
int main()
{
_STUDENT student;
student.scores.benke.nGaokaoScore;
return 0;
}
以上这个例子的使用环境是,开发一个学生管理系统,但是学生有研究生,本科生,博士生,把学生的信息能够用一个结构体表示,这样清晰明了,使用方便。
但是对于研究生,本科生,博士生来说,他们既有相同的信息,又有不同的信息。假定都只能有一种身份,他是本科生,就不可能是博士或者研究生,反之亦然,他们是互斥的。
这种情况就非常适合使用结构体套联合体,联合体再套结构体的方式来表示他们的信息。这样既节省了空间,又把他们集合到了一起。
五、定义新类型
定义新类型名的一般形式是:
typedef 类型名 标识符;
代码示例:
typedef int INT;
typedef int* PINT;
int main()
{
int num = 10;
INT num2 = 20;
PINT pInt = NULL;
pInt = #
printf("%d", *pInt);
}
使用了类型定义之后,就可以用新的名字代替原来的名字定义变量。
注意:使用 typedef 语句不是创造新类型,而是为已经存在的类型增加了一个新名字。下面是最为常见的使用方式:
typedef struct _PEOPLE
{int nAge;
int nScore;
char szName[20];
}PEOPLE, *PPEOPLE;
int main()
{
//给结构体类型起一个新名字
struct _PEOPLE People1 = { 10, 20, "xiaohong" };
PEOPLE People2 = { 10, 20, "xiaohong"}; //这里使用新名字定义变量
return 0;
}
注意到有一段代码是加粗的,是为了说明前面的标识符struct,为什么会这样的写法呢?
这是一个 C 语言的遗留问题。在 C 语言中,定义结构体变量,必须使用上述代码前面加标识符struct的方式。为了省略一个 struct,所以重定义类型名。这种用法一直用到了现在。并且遗留到了C++中。但是我们知道C++中定义结构体变量本身也不需要加 struct。这不算尴尬,尴尬的是大家现在都知道 C++定义结构体或者联合体变量不需要加 struct/union,很多地方也依然保持着C语言的习惯,尤其在 windows 开发中。