复合类型(自定义类型)目录
1.结构体
1.1概述
数组:描述一组具有相同类型数据的有序组合,用于大量相同类型的数据运算。
有时候我们需要将不同类型的数据组合成一个有机的整体,如一个学生有学号,姓名,年龄……等属性,显然单独定义以上变量比较繁琐,数据不便于管理。
C语言给出了另一种构造数据类型——结构体。
结构体基本概念:结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
2结构体的定义和使用
通过结构体创建变量的方式有三种:
(1)struct+结构体名+变量名
(2)struct+结构体名+变量名 ={成员1值,成员2值}
(3)定义结构体时顺便创建变量
三种创建方式示例:
#include<stdio.h>
#include<string.h>
struct student {
char name[20];
int age;
int score;
}stu3={"丽丽",17,88}; //创建方式3:定义结构体时顺便创建变量stu3
int main(){
struct student s1; //创建方式1:struct+结构体名+变量名
//拷贝赋值
strcpy(s1.name,"李四"); //使用用一个"."进行赋值
s1.age = 18 ;
printf("名字:%s 年龄:%d\n",s1.name,s1.age);
//创建方式2:struct+结构体名+变量名 ={成员1值,成员2值}
struct student s2 ={"小米",19,100};
printf("名字%s 年龄%d 成绩%d\n",s2.name,s2.age,s2.score);
printf("名字%s 年龄%d 成绩%d\n",stu3.name,stu3.age,stu3.score);
return 0;
}
示例:结构体的录入
#include<stdio.h>
struct student {
char name[20];
int age;
int score;
char addr[20];
};
int main(){
struct student s;
//注意数组不用取地址,数组变身可表示地址;
scanf("%s %d %d %s",s.name,&s.age,&s.score,s.addr);
printf("名字:%s 年龄%d 成绩%d 地址%s",s.name,s.age,s.score,s.addr);
return 0;
}
3.结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法:struct 结构体名 数组名{元素个数}={ {},{},{} ,{}…… {} }
示例
#include<stdio.h>
#include<string.h>
struct student{ //无sss
char name[20];
int age;
};
int main(){
//数组
struct student sss[3]={ //struct student是数据类型
{"张三",22},
{"李四",21}, //记得得有逗号
{"王五",25}
};
//可修改
strcpy(sss[2].name,"老六");
int i;
for(i = 0; i<3;i++){
printf("姓名%s 年龄%d\n",sss[i].name,sss[i].age);
//sss才是一个数组,不能把[i]写到name后面
}
printf("结构体数组大小:%d\n",sizeof(sss)); //300
printf("结构体元素大小:%d\n",sizeof(sss[0])); //100
printf("结构体元素个数:%d\n",sizeof(sss)/sizeof(sss[0])); //3
return 0;
}
结构体数组冒泡排序:形式改变,原理一样。
同理可以用sizeof(sss)/sizeof(sss[0])表示元素个数。
#include<stdio.h>
#include<string.h>
struct student{
char name[20];
int age;
};
int main(){
//数组
struct student sss[5]={
{"张三",22},
{"李四",21},
{"王五",25},
{"老六",27},
{"老李",18}
};
int i,j;
int len=sizeof(sss)/sizeof(sss[0]);
for(i=0;i<len;i++){
for(j=0;j<len-1-i;j++){
if(sss[j].age>sss[j+1].age){
int temp =sss[j].age ;
sss[j].age =sss[j+1].age ;
sss[j+1].age =temp;
}
}
}
for(i = 0; i<len;i++){
printf("姓名%s 年龄%d\n",sss[i].name,sss[i].age); }
return 0;
}
4.开辟堆区空间存储结构体
在结构体使用malloc
拓展:malloc使用相关:
1、要有头文件#include<stdlib.h>
2、格式 数据类型 变量=(数据类型*)malloc(需要空间个数*字节大小);
例子:int *a =(int*)malloc(number*sizeof(int));
3、申请的空间大小以字节为单位,返回的结果是void*,需要自行转换,如加(int*)
4、最后空间用完返回值为0或者NULL ,则可以结合while语句使用。
5、 向malloc申请空间,需要归还 ,在使用后加free(变量);
示例:
#include<stdio.h>
#include<stdlib.h> //使用malloc加头文件
typedef struct student ss; //起别名,简化
struct student{
char name[20];
int age;
};
int main(){ //区别于之前的是栈区数组
ss* p=(ss*)malloc(sizeof(ss)*3); //此数组存于堆区
int i;
for(i=0;i<3;i++){
scanf("%s %d",p[i].name,&p[i].age);
}
for(i=0;i<3;i++){
printf("姓名%s 年龄%d\n",p[i].name,p[i].age);
}
free(p);
return 0;
}
5.结构体嵌套结构体
作用:结构体成员可以是另一个结构体
例如:结构体学生,内部结构体是其分数
#include<stdio.h>
struct score{
int yuwen;
int shuxue;
};
struct student{
int age;
char name[20]; //定义字符串用数组
struct score ss;
};
int main(){
struct student a={22,"张三",100,98};
printf("年龄%d 姓名%s 语文%d 数学%d\n",a.age,a.name,a.ss.yuwen,a.ss.shuxue);
//计算字节大小
printf("%d\n",sizeof(a)); //32=4+20+8
printf("%d\n",sizeof(a.ss)); //8 =4+4
printf("%d\n",sizeof(a.ss.shuxue)); //4
printf("%d\n",sizeof(struct student)); //32
return 0;
}
6.结构体与指针
6.1结构体成员为指针类型
结构体成员为指针,直接用strcpy赋值会报错,可以通过malloc开辟一个空间,
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//结构体成员为指针
struct student{
char*name;
int age;
int*score;
};
int main(){
struct student s1;
//strcpy(s1.name,"李四"); // 报错
s1.name=(char*)malloc(sizeof(char)*2);
//s1.age=(int*)malloc(sizeof(int)*4); //错
s1.score=(int*)malloc(sizeof(int)*4); //指针才能申请空间
strcpy(s1.name,"张三");
strcpy(s1.name,"李四");
s1.score[1]=22;
s1.score[2]=21;
printf("%s\n",s1.name);
printf("%d",s1.score[1]);
free(s1.name); //记得最后释放空间
free(s1.score);
return 0;
}
6.2结构体指针
作用:通过指针访问结构体中的成员
写法一:(*p).name (括号和点)
写法二: p->name (减号和大于号)
示例:
#include<stdio.h>
struct student{
char name[20];
int age;
};
//结构体指针
int main(){
struct student ss;={"老六",23};
struct student*p=&ss;
//*p->name ="张三"; //报错
(*p).age=19;
printf("%s\n",p->name); //两种写法皆可
printf("%s\n",(*p).name);
printf("%d\n",(*p).age);
return 0;
}
6.3开辟指针的堆区内存
先开辟3个指针内存,存储3个结构体,再结合for循环语句依次给结构体内部指针开辟堆区内存
#include<stdio.h>
#include<stdlib.h>
typedef struct student ss;
struct student{
char* name; //指针8字节
int* age; //4字节
};
//结构体指针
int main(){
ss*p=(ss*)malloc(sizeof(ss)*3);
int i;
for(i=0;i<3;i++){ //
//(p+i)->name=(char*)malloc(sizeof(char)*3);
//(p+i)->age==(char*)malloc(sizeof(int)*3);//3个人
//或者写成p[i] 降维度操作,不表示指针
p[i].name=(char*)malloc(sizeof(char)*3);
p[i].age=(char*)malloc(sizeof(int)*3);
//注意返回的还是指针不要写成 p[i].age=(int*)malloc(sizeof(int)*3)
}
for(i=0;i<3;i++){
scanf("%s %d",p[i].name,&p[i].age);
}
for(i=0;i<3;i++){
printf("姓名%s 年龄%d\n",(p+i)->name,(p+i)->age);
}
for(i=0;i<3;i++){
free(p[i].name); //先释放内部再释放外部p
free(p[i].age);
}
free(p);
return 0;
}
运行结果如下
7.结构体做函数参数
7.1结构体普通变量做函数参数
7.2结构体指针变量做函数参数
7.3结构体数组名做函数参数
7.4const修饰的结构体指针与二级指针
其实原理都一模一样,合并写代码如下:
#include<stdio.h>
#include<string.h>
struct student{
char name[20];
int age;
};
void text1(struct student ss){
printf("%s\n",ss); //张三
}
void text2(struct student*p){
strcpy(p->name,"李四");
printf("%s\n",p->name); //李四
}
//数组作为函数参数退化为指针,丢失元素精度,需要传递个数
void text3(struct student sss[3],int len){
int i,j;
for(i=0;i<len;i++){
for(j=0;j<len-1-i;j++){
if(sss[j].age>sss[j+1].age){
int temp =sss[j].age ;
sss[j].age =sss[j+1].age ;
sss[j+1].age =temp;
}
}
for(i=0;i<len;i++){
printf("姓名%s 年龄%d\n",sss[i].name,sss[i].age);
}
}
}
int main(){
struct student ss={"张三",30};
text1(ss);
struct student*p=&ss;
text2(p);
printf("%s\n",p->name); //也打印李四——同理可以改变实参
struct student sss[3]={
{"张三",22},
{"李四",21},
{"王五",25}
};
int len=sizeof(sss)/sizeof(sss[0]);
text3(sss,len);
const struct student*const p1=&ss;
struct student**pp=&p1;
(*pp)->age=888;
//二级指针的使用可以对变量的值进行修改
printf("姓名%s 年龄%d\n",ss.name,ss.age); //打印张三 888
return 0;
}
2.(共用体)联合体
概念:
1、联合union是一个能在同一个存储空间存储不同类型数据的类型;
2、联合体所占的内存长度等于其最长成员的长度倍数;
3、同一存段可以用来存放几种不同类型的成员,但每一瞬间只有一种起作用;
4、共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
5、共用体变量的地址和它的各成员的地址是同一个地址
示例如下:
#include<stdio.h>
union Var{
int a;
float b;
char c;
double d;
long long e;
};
int main(){
union Var vv;
vv.a=80;
printf("%d\n",vv.a);
vv.b =8;
printf("%d\n",vv.a);//乱码
printf("%d\n",sizeof(vv)); //8
printf("%p\n",&vv.a ); //地址均为000000000062FE10
printf("%p\n",&vv.b );
printf("%p\n",&vv.c );
printf("%p\n",&vv.d );
return 0;
}
3.枚举
概念:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内
#include<stdio.h>
enum COLOR{
red=10,yellow=20,blue=30,green=40
}color;
int main(){
int value;
while(1){
scanf("%d",&value);
switch(value){
case red:
printf("红色");
break;
case yellow:
printf("黄色") ;
break;
case blue:
printf("蓝色");
break;
case green:
printf("绿色");
break;}
}
return 0;
}
4.typedef
概念:typedef为C语言的关键字,作用是为一种数据类型定义新名字