C++笔记day9-复合类型

189 阅读5分钟

结构体定义及赋值

定义结构体变量的方式:

    l  先声明结构体类型再定义变量名

    2  在声明类型的同时定义变量

    3  直接定义结构体类型变量(无类型名)


   结构体类型和结构体变量关系:

    l  结构体类型:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元。

    2  结构体变量:系统根据结构体类型(内部成员状况)为之分配空间。

1、复合类型在内存中存放的模型:

image.png

定义结构体格式:
    struct 结构体名称
    {
        结构体成员列表;
    };
       定义结构体变量
    struct 结构体名称 结构体变量名
    结构体变量名.结构体成员列表 = 值;
    如果是字符串类型,需要使用strcpy拷贝。

示例

//声明结构体类型
struct students
{
	//成员列表
	char name[21];
	unsigned int age;
	char tel[16];
	float scores[3];
	char sex;
};

int main(void)
{
	//按照结构体顺序 赋值;
	//struct students stu = { "张三",50, "13812341234",100.0f,200,300,'M' };
	//不按顺序赋值的形式
	//struct students stu = {.sex = 'M', .name = "刘能", .tel ="13712241234",.scores[0]=100}
	struct students stu;
	//stu.name = "刘能"; 这样会被定义为常量,会出错
	strcpy(stu.name, "刘能");
	stu.age = 50;
	strcpy(stu.tel, "13112344123");
	stu.scores[0] = 90;
	stu.scores[1] = 88;
	stu.scores[2] = 89;
	stu.sex = 'F';

	printf("姓名:%s\n", stu.name);
	printf("年龄:%u\n", stu.age);
	printf("电话:%s\n", stu.tel);
	printf("分数:%.1f %.1f %.1f\n", stu.scores[0], stu.scores[1], stu.scores[2]);
	printf("性别:%s\n", stu.sex == 'M' ? "男" : "女");

	system("pause");
	return EXIT_SUCCESS;
}

结构体大小和内存存储结构

结构体在程序中别的文件中也可以使用,也可以把结构体写在.h文件中

内存结构模型图

image.png

示例

//结构体需要根据数据类型进行内存对齐;
struct stus 
{
	//成员列表
	char name[20];
	unsigned int age;
	char tel[15];
	float scores[3];
	char sex; //占56字节
} stu;// = { "尼古拉斯",500,"13012341234",100.0f,200,300,'M' };

struct stus1
{
	//成员列表
	char name[20];
	unsigned int age;
	char tel[15];
	char sex;
	float scores[3];//占52字节;会跟成员列表中占据内存最长的进行对齐;
} stu1;//在结构体构建中合理调整成员列表可以减少占用的内存空间;
//一般情况下结构体里排列数据类型的时候,从上到下按照数据类型从大到小排列即可;
//要找到利于阅读和节省空间的平衡点;
int main(void)
{
	printf("结构体stu大小%d\n", sizeof(stu));
	printf("结构体stu1大小%d\n", sizeof(stu1));

	system("pause");
	return EXIT_SUCCESS;
}

结构体数组

正常按照数组定义就可以,如果有输入的话要注意格式问题。

示例

struct stus1
{
	//成员列表
	char name[20];
	unsigned int age;
	char tel[15];
	char sex;
	float scores[3];
};
int main(void)
{
	struct stus1 s[2];
	for (size_t i = 0; i < 2; i++)
	{
		printf("请您输入 姓名 年龄 电话 成绩 性别:\n");
		scanf("%s%d%s%f%f%f,%c", s[i].name, &s[i].age, s[i].tel, &s[i].scores[0], 
			&s[i].scores[1], &s[i].scores[2], &s[i].sex);
	}
	for (size_t i = 0; i < 2; i++)
	{
		printf("姓名:%s\n", s[i].name);
		printf("年龄:%u\n", s[i].age);
		printf("电话:%s\n", s[i].tel);
		printf("分数:%.1f %.1f %.1f\n", s[i].scores[0], s[i].scores[1], s[i].scores[2]);
		printf("性别:%s\n", s[i].sex == 'M' ? "男" : "女");
	}
	system("pause");
	return EXIT_SUCCESS;
}

结构体排序

示例

struct students 
{
	//成员列表
	char name[21];
	float scores[3];
};

int main(void)
{
	struct students s[3];
	for (size_t i = 0; i < 3; i++)
	{
		printf("请输入学生姓名 成绩 : \n");
		scanf("%s%f%f%f", s[i].name, &s[i].scores[0], &s[i].scores[1], &s[i].scores[2]);
	}
	//冒泡排序
	for (size_t i = 0; i < 3-1; i++)
	{
		for (size_t j = 0; j < 3-i-1; j++)
		{
			int temp;
			int sum1 = s[j].scores[0] + s[j].scores[1] + s[j].scores[2];
			int sum2 = s[j+1].scores[0] + s[j+1].scores[1] + s[j+1].scores[2];
			if (sum1>sum2)
			{
				//结构体交换,交换所有成员列表中的数据
				//交换姓名
				char temp[21] = { 0 };
				strcpy( temp,s[j].name );
				strcpy(s[j].name, s[j + 1].name);
				strcpy(s[j + 1].name, temp);
				//交换成绩
				for (size_t k = 0; k < 3; k++)
				{
					float temp = s[j].scores[k];
					s[j].scores[k] = s[j + 1].scores[k];
					s[j + 1].scores[k] = temp;
				}
			}

		}
	}
	for (size_t i = 0; i < 3; i++)
	{
		printf("姓名:%s\n", s[i].name);
		printf("成绩:%.1f %.1f %.1f \n", s[i].scores[0], s[i].scores[1], s[i].scores[2]);
	}
	system("pause");
	return EXIT_SUCCESS;
}
上述代码中结构体交换所有成员列表中的数据非常麻烦,也可以采取结构体变量交换的简单方式。
相当于交换了里边的地址。

示例2:结构体排序优化

struct students
{
	//成员列表
	char name[21];
	float scores[3];
};

int main(void)
{
	struct students s[3];
	for (int i = 0; i < 3; i++)
	{
		printf("请输入学生姓名 成绩 : \n");
		scanf("%s%f%f%f", s[i].name, &s[i].scores[0], &s[i].scores[1], &s[i].scores[2]);
	}
	//冒泡排序
	for (int i = 0; i < 3 - 1; i++)
	{
		for (int j = 0; j < 3 - i - 1; j++)
		{
			int temp;
			int sum1 = s[j].scores[0] + s[j].scores[1] + s[j].scores[2];
			int sum2 = s[j + 1].scores[0] + s[j + 1].scores[1] + s[j + 1].scores[2];
			if (sum1 > sum2)
			{
				//结构体交换,结构体变量交换
				struct  students  temp = s[j];
				s[j] = s[j + 1];
				s[j + 1] = temp;
			}

		}
	}
	for (int i = 0; i < 3; i++)
	{
		printf("姓名:%s\n", s[i].name);
		printf("成绩:%.1f %.1f %.1f \n", s[i].scores[0], s[i].scores[1], s[i].scores[2]);
	}
	system("pause");
	return 0;
}

#结构体与指针

结构体成员为指针

image.png

示例1

struct stuinfo 
{
	char* name;
	int age;
};

int main(void)
{
	struct stuinfo si;
	si.name = (char*)malloc(sizeof(char) * 21);//开辟堆空间
	
	strcpy(si.name, "张三");//结构体内成员列表赋值
	si.age = 18;

	printf("%s %d\n", si.name, si.age);
	free(si.name);

	system("pause");
	return EXIT_SUCCESS;
}

示例2:结构体排序的指针写法

struct students
{
	//成员列表
	char * name; //指针写法
	float scores[3];
};

int main(void)
{
	struct students s[3];
	for (int i = 0; i < 3; i++)
	{
		//开辟堆空间;
		s[i].name = (char*)malloc(sizeof(char) * 21);

		printf("请输入学生姓名 成绩 : \n");
		scanf("%s%f%f%f", s[i].name, &s[i].scores[0], &s[i].scores[1], &s[i].scores[2]);
	}
	//冒泡排序
	for (int i = 0; i < 3 - 1; i++)
	{
		for (int j = 0; j < 3 - i - 1; j++)
		{
			int temp;
			int sum1 = s[j].scores[0] + s[j].scores[1] + s[j].scores[2];
			int sum2 = s[j + 1].scores[0] + s[j + 1].scores[1] + s[j + 1].scores[2];
			if (sum1 > sum2)
			{
				//结构体交换,结构体变量交换
				struct  students  temp = s[j];
				s[j] = s[j + 1];
				s[j + 1] = temp;
			}

		}
	}
	for (int i = 0; i < 3; i++)
	{
		printf("姓名:%s\n", s[i].name);
		printf("成绩:%.1f %.1f %.1f \n", s[i].scores[0], s[i].scores[1], s[i].scores[2]);
	}
	for (size_t i = 0; i < 3; i++) //释放堆空间
	{
		free(s[i].name);
	}
	system("pause");
	return 0;
}

结构体指针

image.png

示例

struct stuinfo
{
	char* name;
	int age;
} stu;

int main(void)
{
	struct stuinfo* s = &stu;
	s->name = (char *)malloc(sizeof(char) * 21);//->指向结构体指针地址运算符
	
	strcpy(s->name, "李飞");
	s->age = 50;
	printf("%s %d\n", s->name, s->age);
	
	free(s->name);
	system("pause");
	return EXIT_SUCCESS;
}

堆空间开辟结构体

image.png

示例

struct stuinfo
{
	char* name;
	int age;
} st;

int main(void)
{
	struct stu;
	struct stuinfo* stu = (struct stuinfo*)malloc(sizeof(st));
	//此处上面的*stu不能和后面sizeof内的变量名重名会冲突;造成申请的堆空间不够;
	stu->name = (char*)malloc(sizeof(char) * 21);

	strcpy(stu->name, "张飞");
	stu->age = 18;

	printf("%s %d\n", stu->name, stu->age);

	if (stu->name)
	{
		free(stu->name);
		stu->name = NULL;
	}
	if (stu)
	{
		free(stu);
		stu = NULL;
	}

	system("pause");
	return EXIT_SUCCESS;
}

堆空间开辟结构体对学生成绩排序

示例

struct stuinfo
{
	char* name; //char指针
	float * scores; //浮点数指针
} st;

int main(void)
{
	struct stuinfo* stu = (struct stuinfo*)malloc(sizeof(st) * 3);

	for (int i = 0; i < 3; i++)
	{
		//开辟堆空间;两项成员列表都要开辟
		stu[i].name = (char*)malloc(sizeof(char) * 21);
		stu[i].scores = (float *)malloc(sizeof(float) * 3);
		printf("请输入学生姓名 成绩 : \n");
		scanf("%s%f%f%f", stu[i].name, &stu[i].scores[0], &stu[i].scores[1], &stu[i].scores[2]);
	}

	for (size_t i = 0; i < 3-1; i++)
	{
		for (size_t j = 0; j < 3-i-1; j++)
		{
			struct stuinfo temp;
			int sum1 = stu[j].scores[0] + stu[j].scores[1] + stu[j].scores[2];
			int sum2 = stu[j + 1].scores[0] + stu[j + 1].scores[1] + stu[j + 1].scores[2];
			if (sum1 > sum2) 
			{
				temp = stu[j];
				stu[j] = stu[j + 1];
				stu[j + 1] = temp;
			}
		}
	}
	for (int i = 0; i < 3; i++)
	{
		printf("姓名:%s\n", stu[i].name);
		printf("成绩:%.1f %.1f %.1f \n", stu[i].scores[0], stu[i].scores[1], stu[i].scores[2]);
	}
	//释放空间,两项成员列表都要释放
	for (size_t i = 0; i < 3; i++)
	{
		free(stu[i].name);
		free(stu[i].scores);
	}
	free(stu);
	system("pause");
	return EXIT_SUCCESS;
}

结构体和函数

结构体做函数参数

示例

struct info
{
	char name[21];
	int age;
};
void fun01(struct info s)//结构体作为形参
{
	strcpy(s.name, "李四");
	s.age = 20;
}
void fun02(struct info *s)//结构体指针作为形参
//可以把结构体当成自己定义变量的数据类型存在
{
	strcpy(s->name, "李四");
	s->age = 20;
}

int main(void)
{
	struct info s = { "张三",18 };
	fun01(s);
	printf("%s %d\n", s.name, s.age);
	fun02(&s);
	printf("%s %d\n", s.name, s.age);


	system("pause");
	return EXIT_SUCCESS;
}

结构体做函数返回值

注意:
   结构体指针作为返回值地址已经被销毁,会报错。

示例

struct info fun03() //结构体作为返回值
{
	struct info s;
	strcpy(s.name, "李四");
	s.age = 20;

	return s;
}
struct info* fun04() //结构体指针作为返回值
{
	struct info s;
	strcpy(s.name, "李四");
	s.age = 20;

	return &s;
}
int  main()
{
	struct info s = fun03();
	printf("%s %d\n", s.name, s.age);
	struct info* s = fun04(); //结构体指针作为返回值打印会报错
	printf("%s %d\n", s->name, s->age);

	return 0;
}

结构体函数案例:学生成绩

struct stu 
{
	char* name;
	float* scores;
}st; //此处要提前声明个st,方便后面申请空间;

void bubble(struct stu* stu1, int len) 
{
	for (size_t i = 0; i < len-1; i++)
	{
		for (size_t j = 0; j < len-i-1; j++)
		{
			int sum1 = stu1[j].scores[0] + stu1[j].scores[1] + stu1[j].scores[2];
			int sum2 = stu1[j + 1].scores[0] + stu1[j + 1].scores[1] + stu1[j + 1].scores[2];
			if (sum1 > sum2)
			{
				struct stu temp = stu1[j];
				stu1[j] = stu1[j + 1];
				stu1[j + 1] = temp;
			}
		}
	}
}

int main(void)
{
	struct stu* stu1 = (struct stu*)malloc(sizeof(st) * 3);

	for (int i = 0; i < 3; i++)
	{
		//开辟堆空间;两项成员列表都要开辟
		stu1[i].name = (char*)malloc(sizeof(char) * 21);
		stu1[i].scores = (float*)malloc(sizeof(float) * 3);
		printf("请输入学生姓名 成绩 : \n");
		scanf("%s%f%f%f", stu1[i].name, &stu1[i].scores[0], &stu1[i].scores[1], &stu1[i].scores[2]);
	}
	bubble(stu1, 3);
	for (int i = 0; i < 3; i++)
	{
		printf("姓名:%s\n", stu1[i].name);
		printf("成绩:%.1f %.1f %.1f \n", stu1[i].scores[0], stu1[i].scores[1], stu1[i].scores[2]);
	}
	//释放空间,两项成员列表都要释放
	for (size_t i = 0; i < 3; i++)
	{
		free(stu1[i].name);
		free(stu1[i].scores);
	}
	free(stu1);

	system("pause");
	return EXIT_SUCCESS;
}

结构体嵌套

所占据空间的大小根据结构体中最长的成员对齐。

示例

struct stra
{
	int a;
	float b;
	char c;
};
struct strb 
{
	double d;
	char* e;
	short f;
	struct stra abc; //结构体里b里嵌套了a;
};
int main(void)
{
	struct strb strbb;
	strbb.d - 10.0f;
	strbb.abc.a = 100;//给结构体b里嵌套的a赋值;

	printf("%d\n", strbb.abc.a);//打印结构体b里的a;
	printf("%d\n", sizeof(strbb));
	system("pause");
	return EXIT_SUCCESS;
}

image.png

image.png

共用体(联合体)

l  联合union是一个能在同一个存储空间存储不同类型数据的类型;

2  联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;

3  同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;

4  共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;

5  共用体变量的地址和它的各成员的地址都是同一地址。

示例

//共用体 union 共用体名称 成员列表 共用体变量名;
//整个共用体里所有成员共用一块内存大小
union vars 
{
	double a;
	float b;
	int c;
	short d;
	char f;
} var;
int main(void)
{
	printf("%d\n", sizeof(var)); //大小根据最大数据类型决定,但是会对齐(相当于整个公约数?)
	var.a= 100;
	var.b = 3.14;
	var.c = 66;
	printf("a= %d\n", var.a);
	printf("b= %d\n", var.b);
	printf("c= %d\n", var.c); //共用体中最后一次被赋值的是准确的,其他的值会发生变化;
	system("pause");
	return EXIT_SUCCESS;
}

枚举

枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。

枚举类型定义:
        enum weekday
        {
        sun = 2, mon, tue, wed, thu, fri, sat
        };
l  在枚举值表中应列出所有可用值,也称为枚举元素。

2  枚举值是常量,不能在程序中用赋值语句再对它赋值。

3  举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2 …

示例

enum colors 
{
	red,blue,yellow,black,white,green
}clo;
/*
枚举可以用作过程的锁定,比如银行取钱:也可以用作流程控制;
	插卡,输入密码,锁定,取款,查询,退卡等;
枚举可以赋值,未赋值的会自动根据前面的增加,如:下例中blue=11,black=21,可做区间分隔
enum colors
{
	red =10,blue,yellow=20,black,white,green
}clo;
	*/

int main(void)
{
	int val;
	scanf("%d", &val);
	switch (val)
	{
	case red:
		printf("red\n");
		break;
	case blue:
		printf("blue\n");
		break;
	case yellow:
		printf("yellow\n");
		break;
	case black:
		break;
	case white:
		break;
	case green:
		break;
	default:
		break;
	}
	system("pause");
	return EXIT_SUCCESS;
}

typedef

typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。

l  与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值

2  #define发生在预处理,typedef发生在编译阶段
#include <stdio.h>

typedef int INT;
typedef char BYTE;
typedef BYTE T_BYTE;
typedef unsigned char UBYTE;
typedef struct type

{      UBYTE a;
       INT b;
       T_BYTE c;
}TYPE, *PTYPE;

int main()

{
       TYPE t;
       
       t.a = 254;
       t.b = 10;
       t.c = 'c';

       PTYPE p = &t;

       printf("%u, %d, %c\n", p->a, p->b, p->c);
 
       return 0;
}

打字小游戏

void tips() 
{
	printf("==============打字游戏================\n");
	printf("============按任意键退出===============\n");
	printf("===========按ESC退出游戏==============\n");
	char ch = _getch();
	if (ch == 27) 
	{
		exit(0);
	}
}

void rand_ch(char* arr) 
{
	srand((unsigned int)time(NULL));
	for (size_t i = 0; i < 50; i++)
	{
		arr[i] = rand() % 26 + 'a';
	}
}

void print_ch(char* arr) 
{
	//变量 计时器 开始 结束时间;计数器 val
	int start_time;
	int end_time;
	int val =0;
	for (size_t i = 0; i < 50; i++)
	{
		char ch = _getch();
		if (i == 0) 
		{
			start_time = time(NULL);
		}
		if (ch == arr[i]) 
		{
			printf("%c", ch);
			val++;
		}
		else 
		{
			printf("-");
		}
	}

	end_time = time(NULL);
	printf("\n");
	printf("用时:%d\n", end_time - start_time);
	printf("正确率:%.1f%%\n", val * 1.0 / 50 * 100);
}
int main(void)
{
	//字库
	char arr[51];
	memset(arr, 0, 51);
	while (1)
	{
		//1.提示操作
		tips();
		//2.随机字符
		rand_ch(arr);
		printf("%s\n", arr);
		//3.输入字符
		print_ch(arr);			
	}


	system("pause");
	return EXIT_SUCCESS;
}