结构体-位段-联合体-枚举

时间:2022-09-05 21:01:06

什么是结构体(struct)?

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合,叫做结构。(ps:数组是同一类型元素的集合)

结构体可以被声明为变量,指针或数组等。
结构体的定义
一般形式:

struct Tag {
member list;
}variable list;

其中:struct为结构体关键字,Tag为结构体的标志, member list为结构体成员列表,variable list为结构体声明的变量
需要注意的是,在进行定义的时候,struct , Tag , variable list 这三者至少要出现两个.

举几个栗子:

struct {
char a;
int b;
float c;}s1;

其中:a , b , c 分别为三种不同类型的结构体成员,struct为结构体关键字,s1为声明的结构体变量,这个结构体并没有声明其标签,但这样的书写不好一点是,因为缺少结构体标签,在这之后就不能再用这段代码来定义新的变量了。

struct stu{
char a;
int b;
float c;
};
struct stu t1,t2[10],*t3;

在这里:a , b , c 分别为三种不同类型的结构体成员stu被声明结构体标签,这个结构体里面并没有声明变量,而是在末行用struct stu标签来声明了三个变量。

另外,我们也可以使用typedef来创建新类型,就上面的代码:

typedef struct{
char a;
int b;
float c;
}stu;
//注意:这里的stu为结构体类型,不是变量stu t1,t2[10],*t3;

这里普及一下typedef
typedef是类型定义的意思。typedef struct 是为了使用这个结构体方便。
比如:
若struct node {}这样来定义结构体的话。在申请node 的变量时,需要这样写,struct node n
若用typedef,可以这样写,typedef struct node{}NODE; 在申请变量时就可以这样写,NODE n
**

结构体嵌套问题

**
a.包含其它结构体

struct STU1{
char a;
struct STU2 B;
}A;

b.结构体声明包含指向自己类型的指针

struct STU{
char a;
struct STU *P;
};

c.两个结构体相互包含 ( ps:需要对其中一个结构体进行不完整声明)

struct B;     //对B结构体的不完整声明
struct A{
char a;
struct B *pb;//指向结构体B的指针
};
struct B{
int a;
struct A *pa;//指向结构体A的指针
}

常见错误:

1.struct tag{
2.struct tag A;
3.int value;
4.};

这种声明是错误的,因为这种声明实际上是一个无限循环,成员A是一个结构体,A的内部还会有成员是结构体,依次下去,无线循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。上面的b里面的*p因为是指针,长度是确定的,所以就合法。

使用typedef时:

typedef struct {
int value
NODE *link;
} NODE

由于此时的NODE标签还没声明出来,所以上面的link是非法的。

结构体的访问

两种访问方法: 点(.)式访问 箭头(->)式访问

点(.)式访问 用于结构体变量访问成员;

箭头(->)式访问 用于结构体指针访问成员

举个栗子:

#include<stdio.h>
#include<Windows.h>
int main()
{
typedef struct {
int a;
}stu;
stu STU1;
stu *STU2 = &STU1;//让STU2指针指向STU1结构体
STU1.a = 1;
printf("%d\n", STU1.a);
printf("%d\n", STU2->a);
system("pause");
return 0;
}

结构体的赋值

两种赋值方法: 逐一赋值法 初始化(整体赋值法)

逐一赋值法:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<Windows.h>
int main()
{
struct{
char name[10]; //姓名
int num; //学号
int age; //年龄
char group; //小组
float score; //成绩
}stu1;
//给结构体成员赋值
strcpy(stu1.name, "Tom");
stu1.num = 12;
stu1.age = 18;
stu1.group = 'A';
stu1.score = 136.5;
//读取结构体成员的值
printf(" %s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
system("pause");
return 0;
}

整体赋值法:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<Windows.h>
int main()
{
struct{
char name[10]; //姓名
int num; //学号
int age; //年龄
char group; //小组
float score; //成绩
}stu1= { "Tom", 12, 18, 'A', 136.5 };//初始化时整体赋值
//读取结构体成员的值
printf(" 整体赋值法 :%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
system("pause");
return 0;
}

结构体内存对齐

1.第一个成员在与结构体变量偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
3.结构体总大小为最大对齐数的整数倍
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
ps:对齐数 = 编译器默认对齐数 与 该成员大小的较小值。
(vs中默认的值为8 linux中的默认值为4)

举个栗子:

struct {
char a;//1字节
int b;//4字节
short c;//2字节
};

解析:一开始a占1个字节首先置入内存,到整型b占用4个字节,所以必须从4的整数倍开始放置,所以1后面自动补齐3个字节,从4开始,到短整型的c占2个字节,8刚好是2的整数倍,所以c放完里面正好是10个字节,最后一条结构体自身补齐,因为里面最大对齐数是4,10不是4的整数倍,所以再补齐2个字节。所以这个结构体总大小占12字节。

位段

概念: 位段以位(bit)为单位定义结构体(或共用体)中成员所占存储空间的长度。
含有位段的结构体类型称为位段结构体。
成员类型:三种 类型 int , unsigned int , signed int

定义:type [var]: digits
其中type只能为int,unsigned int,signed int三种类型。位段名称var是可选参数,即可以省略。digits表示该位段所占的二进制位数。

举个栗子:

struct node  
{
unsigned int a:4; //位段a,占4位
unsigned int :0; //无名位段,自个儿占一个字节
unsigned int b:4; //位段b,占4位
int c:32; //位段c,占32位
int :6; //无名位段,占6位
};

注意事项:
(1)一个位段必须存储在同一存储单元(即字)之中,不能跨两个 单元。如果其单元空间不够,则剩余空间不用,从下一个单元起存放该位段。
(2)可以通过定义长度为0的位段的方式使下一位段从下一存储单元开始。
(3)可以定义无名位段。
(4)位段的长度不能大于存储单元的长度。
(5)位段无地址,不能对位段进行取地址运算。
(6)位段可以以%d,%o,%x格式输出。
(7)位段若出现在表达式中,将被系统自动转换成整数。
经典例题:

#define _CRT_SECURE_NO_WARNINGS1
#include<stdio.h>
#include<string.h>
#include<Windows.h>
int main()
{
struct{
unsigned a : 1;//只占1位
unsigned b : 3;//3位
unsigned c : 4;//4位
} bit, *pbit;
//这里取的位数看它所占的位数而定
bit.a = 1;//0000 0001取1位置入内存
bit.b = 7;//0000 0111取3位
bit.c = 15;//0000 111取4位
printf("%d, %d, %d\n", bit.a, bit.b, bit.c);
pbit = &bit;
pbit->a = 0;
pbit->b &= 3;
pbit->c |= 1;
printf("%d, %d, %d\n", pbit->a, pbit->b, pbit->c);
system("pause");
return 0;
}

复合的位运算符&=,相当于:pbit->b=pbit->b&3;
位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值为3)
复合位运算符|=,相当于:pbit->c=pbit->c|1;
其结果为15。

**

联合体

**

概念:联合体union的定义方式与结构体一样,但是二者有根本区别。
在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。
而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度
几点须知:
1)联合体就是一个结构
2)联合体的所有成员相对于基地址的偏移量为0
3)此结构空间要大到总够容纳最“宽”的成员
4)并且,其对其方式要适合于联合体中所有类型的成员
大小容量:
联合体的结构空间要足够大,要等于最长的一个结构变量的空间,但是这个最长的空间要满足以下条件:
1.要大于等于最长的一个结构变量的空间
2.并且要能够整除其他结构变量的数据长度,即所有元素的最小公倍数。

union     
{
float a;
char b[5];
int c;
}fighter;

解析:a占4位,b占5位,c占4位,其中b的位数最大为5,但5并不是其它成员的倍数,所以其联合体大小为最小公倍数8

枚举

概念:一组有共同特性的数据的集合,可以定义为枚举。所谓枚举是指将变量的值一一列举出来,变量只限于列举出来的值的范围内取值。

定义:

enum day{
MON,, TUE, WED, THU, FRI, SAT, SUN
};

(1) 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
(2)day是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无项。
(3) 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
(4) 可以人为设定枚举成员的值,从而自定义某个范围内的整数。
(5) 枚举型是预处理指令#define的替代。
(6) 类型定义以分号结束。

使用建议:
a.当参数、返回值、变量等类型可以使枚举,尽量使用枚举(要注意考虑分类的稳定性)
b.大多数情况下都可以使用int类型枚举,下列情况除外。
c.枚举可能被大量频繁的使用,这时为了节约空间可以使用小于int类型的枚举。

上面代码结尾运行后的代码,由于种种不可抗拒的因素,运行结果图没能放上去。
这些大概就是在学习结构体部分所掌握的知识点,希望对大家有所帮助。