我把这三个自定义数据类型放在一起进行一个对比和记忆
一、结构体
当我们需要记录一个人或者事物的信息时,我们要记录他(它)的一些方面的信息。
比如说一个人的时候,我们需要知道他的名字,年龄,性别,电话号码等内容;
但是单靠以前我们学过的int ,char,float ,double....只能描述其中的一方面,
如果选择我们需要的几个类型,并把它集中起来,可以用来描述一个人或者事物的相关信息。
这时候结构体就起到了作用!!
首先,结构体的关键字是struct
那么如何定义一个结构体,又或者说结构体的格式是什么样子的?
1.结构体的格式
struct B
{
char c;
short s;
double d;
};
这里我列举了一个结构体类型。
格式为在struct关键字后面加上 该结构体类型名
在花括号内,是不同的类型的集合。
最后用;结束。
联系我们以前学的数组,
int a[20]={0};
char a[]="abcd";
一个变量里的类型都是一样的,要么是int类型,要么是char类型。
这里的结构体类型中,其中包含的类型可以相同,可以不同。
2.结构体的初始化
这里我们写一个关于同学信息的结构体变量
struct stu
{
//结构的成员变量
char name[20];//名字c
int age;//年龄
char id[20];//学号
};
首先给这个结构体类型编一个变量名a或者b等等。。。
这里我们用s
struct stu s = { "张三",30,"20200534"};
初定义时,用花括号括起来,在里面,根据前面的结构体类型里面的几个类型输入相应的变量,并用逗号隔开。这就完成了定义。
3.如何打印(展示)定义的内容
这时候需要用到的操作符是 " . "
看下列代码,假如我们要打印这个张三同学的各个信息
printf("%c\n", s.name);
printf("%d\n",s.age);
printf("%s\n",s.id);
找到变量名s
在用 . 进行索引,找到相应的变量名,就可以了。
当把指针引入其中。
创建一个指针变量 *ps
指向的内容是 struct stu
所以结构体指针的写法为:
struct stu *ps=&s;
当用指针找到相应的结构体变量时
这时候也需要用到一个新的操作符:->
比如现在我们用指针变量ps指向s;
用指针打印出相应的内容:
struct stu *ps=&s;
printf("%c", ps->sb.c);
printf("%c\n",(*ps).sb.c);
同时对ps进行解引用,相当于又回到s.
4.结构体内容占内存大小(结构体对齐)
struct B
{
char c;
short s;
double d;
};
如何计算上面结构体的大小呢?
如果我们猜测一下:char类型占1个字节,short占2个字节,double占8个字节
加起来之后是11个字节。
如图:
错!
这里涉及到偏移量问题。
1.第一个成员在与结构体变量偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
3.结构体整体的大小是各个类型最大对齐数的倍数
对齐数=编译器默认的一个对齐数与该成员占字节大小的较小值
用我的话描述一下这个过程:
像上图一样从第一个格子开始数,char类型占一个字节,就一个绿色。
当short类型开始排队时,应该从2的倍数处开始排。
占2个格子之后到第4个。
然后排double类型 (占8个字节),但是short后面是4.需要放弃4个格子,从8开始,占8个,最后是16个。
因为16是8的倍数。所以不再舍弃字节。
如图:
5.结构体数组
在以前的数组学习中。
比如我们建立一个整型数组:
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
数组里面存放的是一个个数,
而放在一个结构体中,数组中的每一项代表一个结构体变量名。
比如说:
struct stu
{
char name[20];
int age;
char id[30];
}a[3];
这里的a[3]代表三个变量名 分别是a[0],a[1],a[2]。
在这里相当于上面的struct stu s中的s
二. 枚举
1.枚举的格式
如何编写一个枚举的格式,这和结构体类型非常相似,但有一点是不同的。
看下列代码:
enum color
{
red,
green,
blue
};
可以看出,和结构体再外表结构上不同的是,枚举类型把分号改成了逗号。
2.枚举类型中的变量
说是一个个变量,其实每一个变量都是代表的一个个数字常量
我们打印上面的red,green,blue 观察结果
enum color
{
red,
green,
blue
};
int main()
{
enum color s = red;
printf("%d %d %d \n", red, green, blue);
return 0;
}
结果为 0,1,2。
这好像和我们以前学的define有点相似:
#define W 1
#define Y 2
#define Z 3
int main()
{
printf("%d %d %d", W, Y, Z);
return 0;
}
代码结果为:1 ,2 ,3。
那为啥还要用枚举常量?
1.枚举更具有可读性,更加的容易理解
2.提高代码维护性,确保变量合法(可自行体会)
2.枚举常量的赋值
是不是其中的变量只能从0开始?
其实不然。
我们可以给他定义一个数字
这里跟define一样的意思。
enum color
{
red=8,
green,
blue=9
};
其中一种上述情况,当green没有初定义时,会再上一个常量的基础上+1 ,此时green也就是9。
第一个常量没有定义时,默认为0.
三、联合体
1.联合体格式
联合体的格式和结构体的差不多。在结构体的基础上将关键字struct改为union关键字即可。
但是和结构体不同的是,其中的变量通常为 int ,unsigned int,signed int ,char。
union UN
{
char c;//1
int i;//4
};
int main()
{
union UN u;
printf("%d ", sizeof(u));
printf("%p ", &u);
printf("%p ", &(u.c));
printf("%p ", &(u.i));
return 0;
}
同时联合体也叫做共用体。
为何叫做共用体,其实是有说法的。
其实,联合体叫做共用体是因为char类型变量和int类型变量公用一块内存空间。
运行上述代码,可以观察到u的大小为4,是int类型的占字节大小。。。。
所以,联合体所占空间最小是变量中最大占字节数。
同时运行结果为u 和 u.c 和u.i的地址相同。
当然,不是说两个数同时存在同一个地方,这不合理。
在引用的时候,只能定义一个数,另一个数根据占字节大小,大小为另一个数的其中一部份的大小
具体例子为:
union un
{
char c;
int i;
};
int main()
{
union un u;
u.i = 10000;
printf("%d %d", u.c, u.i);
return 0;
}
运行结果为 16 和10000;
2.判断当前处理器的大小端问题
什么是大小端?
举个例子
r如图:
#include<stdio.h>
int check_sys()
{
int a = 0x11223344;//16为进制
char* p = (char*)&a;
return *p;
}
int main()
{
if(check_sys() == 68)//4*16+4
{
printf("小端\n");
}
else
{
printf("大端\n");
}
}
因为这里是*p=68 ,所以符合小端储存模式