目录
C Struct和union
struct,结构体
我们已经对数组很熟悉了,它可以用来存储一组数据类型相同的数据,但实际上,我们常常需要的一组数据里面,数据类型很可能不同,比如,对于一个学生的信息,姓名为字符串,学号为整数,成绩为浮点数,这个时候,数组很难满足我们存储数据的需求,而这就引出了结构体的概念。
结构体是由用户自己指定的一种数据结构,它是多种类型数据的集合,以便引用,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体,语法如下
struct struct_name
{
typename1 var1;
typename2 var2;
};
我们可以看到,我们使用关键字struct
来定义结构体,后面跟着结构体名称,结构体内容即为我们需要的数据成员。
我们来看一个示例
#include <stdio.h>
struct student
{
char *name;
int age;
float socre;
}student1;
int main()
{
struct//无名结构,常出现在main函数中
{
char *name;
int age;
int ID;
}person1;
student1.name="ming";
student1.age=12;
student1.socre=99.5;
printf("学生的姓名为%s\n",student1.name);
printf("学生的年龄为%d\n",student1.age);
printf("学生的成绩为%.2f\n",student1.socre);
person1.name="hong";
person1.age=22;
person1.ID=1234;
printf("小红的姓名为%s\n",person1.name);
printf("小红的年龄为%d\n",person1.age);
printf("小红的身份证号为%d\n",person1.ID);
//使用结构体名称定义新的结构体变量
struct student s1={"a",13,99.6};
printf("新学生的姓名为%s\n",s1.name);
printf("新学生的年龄为%d\n",s1.age);
printf("新学生的成绩为%.2f\n",s1.socre);
return 0;
}
运行结果如下
学生的姓名为ming
学生的年龄为12
学生的成绩为99.50
小红的姓名为hong
小红的年龄为22
小红的身份证号为1234
新学生的姓名为a
新学生的年龄为13
新学生的成绩为99.60
定义结构体的两种方式
在示例代码中,我们看到了两种定义结构体的方式
第一种
struct struct_name
{
//定义结构体成员
}结构体变量1={/*初始化结构体成员*/},结构体变量2;
第一种定义方式是我们前面提到的,后面可以紧接着定义该类型结构体的变量,也可以不定义,而是选择用struct
关键字定义结构体变量,如下
struct struct_name s1={/*初始化结构体成员*/};
或者
struct strcut_name s1;
他表示定义struct_name类型的变量s1;
第二种结构体定义方式
struct
{
//定义结构体成员
}结构体变量1={/*初始化结构体成员*/},结构体变量2;
第二种定义方式称作无名结构体,一般用于main函数,因为它没有结构体名称,所以它是一次性的,必须在定义结构体时在后面定义结构体变量,后面无法再定义相同类型的结构体变量。
应用在结构体上的函数
我们可以定义对结构体变量进行改变的函数,原理还是指针和取地址符,不再多说
#include <stdio.h>
struct student
{
char *name;
int age;
int height;
};
void better(struct student* s)
{
if((*s).age>20)
{
(*s).age=18;
}
if((*s).height<180)
{
(*s).height=180;
}
}
int main()
{
struct student ming={"ming",21,173};
printf("原来的ming:\n");
printf("姓名为%s\n",ming.name);
printf("年龄为%d\n",ming.age);
printf("身高为%d\n",ming.height);
better(&ming);
printf("\n变得更好后的ming:\n");
printf("姓名为%s\n",ming.name);
printf("年龄为%d\n",ming.age);
printf("身高为%d\n",ming.height);
return 0;
}
结果如下
原来的ming:
姓名为ming
年龄为21
身高为173
变得更好后的ming:
姓名为ming
年龄为18
身高为180
union,共用体/联合体
union和struct的关系
union是在某种程度上和结构体很类似的一种数据结构,它也同样是多种类型数据的集合。但他们也有明显的区别。
- 在结构体中,所有成员变量可以同时存在,全面,所以很方便;但不管用不用这些成员变量,内存空间都会分配,容易造成浪费,特别是在内存比较紧张的情况下。
- 在联合体中,虽然也可以定义多个成员变量,但同时只能有一个成员带有值,这就是它的缺点——不够包容;但优点就是内存使用更加精细灵活,节省内存。
为什么联合体是这样的?因为联合体的所有成员变量都是使用同一个内存位置来存储值,自然同一时间只能有一个成员变量有值。
定义union
我们使用union关键字来定义联合体,方式与定义结构体时类似。union 语句定义了一个新的数据类型,带有多个成员。union 语句的格式如下:
union union_name
{
//定义成员变量
}联合体变量1,联合体变量2;
看一个示例
#include <stdio.h>
union person
{
int age;
int height;
}p1;
int main()
{
union person p2={175};
p1.age=13;
p1.height=175;
printf("年龄为%d,地址为%p\n",p1.age,&p1.age);
printf("身高为%d,地址为%p\n",p1.height,&p1.height);
// p2.height=175;
// p2.age=13;
printf("年龄为%d\n",p2.age);
printf("身高为%d\n",p2.height);
return 0;
}
执行结果如下
年龄为175,地址为0x555555558014
身高为175,地址为0x555555558014
年龄为175
身高为175
我们显然可以看到,成员变量age和height共用一个内存地址,并且各种变量名都可以同时使用,操作也是共同生效。
同时要注意的是,无法在定义联合体变量时同时赋值多个成员变量。
使用union来判断电脑是大端还是小端
#include<stdio.h>
union var
{
char c[4];
int i;
};
int main()
{
union var data;
data.c[0]=0x04;//因为是char类型,数字不要太大,算算ascii的范围~
data.c[1]=0x03;//写成16进制为了方便直接打印内存中的值对比
data.c[2]=0x02;
data.c[3]=0x11;
//数组中下标低的,地址也低,按地址从低到高,内存内容依次为:04,03,02,11。总共四字节!
//而把四个字节作为一个整体(不分类型,直接打印十六进制),应该从内存高地址到低地址看,0x11020304,低位04放在低地址上。
printf("%x\n",data.i);
}
输出为11020304
,说明电脑为小端
union的本质
联合体union的本质是在内存中开辟一个足够大的空间,至于怎么放数据,放什么数据,不重要。
union的成员变量是相当于开辟了几个接口(即union包含的变量)!但是,没开辟也能用!
#include <stdio.h>
union u
{
int i;
double d;//这个union有8个字节大小
}uu;
int main()
{
uu.i=10;
printf("%d\n",uu.i);
char *c;
c=(char*)&uu;//把union的首地址赋值、强转成char类型
c[0]='a';
c[1]='b';
c[2]='c';
c[3]='\0';
c[4]='d';
c[5]='e';
printf("%s\n",c);//利用结束符'\0'打印字符串"abc"
printf("%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5]);
return 0;
}
结果如下
10
abc
abcde
这个示例说明,联合体只是定义了int和double接口,只要获得地址,往里面扔什么数据都可以。这就是C语言的强大,这就是union的本质——只管开辟一段空间。