并非从0开始的c++ day8

news2024/10/1 5:34:46

并非从0开始的c++ day8

  • 结构体
    • 结构体嵌套二级指针练习
    • 结构体偏移量
  • 内存对齐
    • 内存对齐的原因
    • 如何内存对齐
  • 文件操作
    • 文件的概念
    • 流的概念
      • 文本流
      • 二进制流
      • 文件缓冲区
      • 文件打开关闭
        • 文件关闭fclose
      • 文件读写函数回顾
      • 按格式化读写文件
      • 文件读写注意事项

结构体

结构体嵌套二级指针练习

需求:一个老师数组,老师设计一个结构体,需要有老师的名字和一个学生数组,学生数组可以有若干个学生

//结构体设计
struct Teacher
{
	//老师姓名
	char* name;

	//老师带的学生姓名数组
	char** students;
};

void allocateSpace(struct Teacher *** teacherArray)
{
	if (teacherArray == NULL)
	{
		return;
	}
	//堆区分配内存
	struct Teacher** ts = malloc(sizeof(struct Teacher*) * 3);

	//数据赋值
	//给老师分配内存
	for (int i = 0; i < 3; i++)
	{
		//给老师分配内存
		ts[i] = malloc(sizeof(struct Teacher));

		//给老师姓名属性 分配内存
		ts[i]->name = malloc(sizeof(char) * 64);

		//给老师姓名赋值
		sprintf(ts[i]->name, "Teacher_%d", i+1);
		

		//给老师带领学生数组分配内存
		ts[i]->students = malloc(sizeof(char*) * 4);

		//给学生姓名分配内存 并且赋值
		for (int j = 0; j < 4; j++)
		{
			ts[i]->students[j] = malloc(sizeof(char) * 64);

			sprintf(ts[i]->students[j], "%s_student_%d", ts[i]->name, j+1);
		}
	}

	//建立关系
	*teacherArray = ts;
}

//打印操作
void printTeacherArray(struct Teacher ** teacherArray)
{
	for (int i = 0; i < 3; i++)
	{
		//老师姓名
		printf("%s\n", teacherArray[i]->name);

		for(int j = 0;j<4;j++)
		//老师带领的学生
		printf("	%s\n", teacherArray[i]->students[j]);
	}
}

//释放堆区数据
void freeSpace(struct Teacher** teacherArray)
{
	if (teacherArray == NULL)
	{
		return;
	}
	
	for (int i = 0; i < 3; i++)
	{
		//释放老师姓名
		if (teacherArray[i]->name != NULL)
		{
			free(teacherArray[i]->name);
			teacherArray[i]->name = NULL;
		}

		//释放学生姓名
		for (int j = 0; j < 4; j++)
		{
			if (teacherArray[i]->students[j] != NULL)
			{
				free(teacherArray[i]->students[j]);
				teacherArray[i]->students[j] = NULL;
			}
		}
		//释放学生数组
		free(teacherArray[i]->students);
		teacherArray[i]->students = NULL;

		//释放老师
		free(teacherArray[i]);
		teacherArray[i] = NULL;
	}

	//释放老师数组
	free(teacherArray);
	teacherArray = NULL;
}

void test01()
{
	//老师数组创建
	struct Teacher** teacherArray = NULL;

	//分配内存
	allocateSpace(&teacherArray);

	//打印所有老师和学生信息
	printTeacherArray(teacherArray);

	//释放堆区数据
	freeSpace(teacherArray);
	teacherArray = NULL;
}

牢记几个malloc对应几个free

结构体偏移量

printf(“b的偏移量:%d\n”, (int)&(p->b) - (int)p);
printf(“b的偏移量: %d\n”, offsetof(struct teacher, b));

struct  teacher
{
	char a;  //0 ~ 3
	int b;	 //4 ~ 7

};

void test01() 
{
	struct teacher t1;
	struct teacher* p = &t1;

	printf("b的偏移量:%d\n", (int)&(p->b) - (int)p);

	printf("b的偏移量: %d\n", offsetof(struct teacher, b));
}

通过偏移量获取数

void test02()
{
	struct teacher t1 = {'a',1000};
	struct teacher* p = &t1;

	printf("b的值为: %d\n", *(int*)((char*)p + offsetof(struct teacher, b)));
	printf("b的值为: %d\n", ((struct teacher *)((int*)p + 1))-> b);
}

//结构体2
struct teacher2
{
	char a;
	int b;


	struct teacher c;
};

void test03()
{
	struct teacher2  t = { 'a',10,'b',20 };

	int offset1 = offsetof(struct teacher2, c);
	int offset2 = offsetof(struct teacher, b);



	printf("属性c中的值为: %d\n", *(int *)((char*)&t + offset1 + offset2));

	printf("属性c中的值为: %d\n", *(int*)((char*)&t + offset1 + offset2));

	
}

内存对齐

访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列,而不是简单地顺序排列,这就是内存对齐

内存对齐的原因

我们知道内存的最小单元是一个字节,当CPU从内存中读取数据的时候,是一个一个字节读取

实际上cpu将内存当成多个块,每次从内存中读取一个块,这个块的大小可能是2、4、8、16等

内存对齐是操作系统为了提高访问内存的策略。操作系统在访问内存的时候,每次读取一定长度(这个长度是操作系统默认的对齐数,或者默认对齐数的整数倍)。如果没有对齐,为了访问一个变量可能产生二次访问

内存对齐优点:以空间换时间

如何内存对齐

  • 对于标准数据类型,它的地址只要是他的长度的整数倍
  • 对于非标准数据类型,比如结构体,要遵循一下对齐原则

内存对齐原则
第一个属性开始 从0开始计算偏移量
第二个属性 要放在该属性的大小 与 对齐模数比 取小的值的 整数倍上
当所有属性都计算完毕之后,整体做二次偏移,将上面计算的结构体
将上面计算的结果 扩充到 这个结构体中最大数据类型的整数倍 与对齐模数比 取小的值 的整数倍
对齐模数默认为8

#pragma pack (show)//查看对齐模数的值 默认为8
#pragma pack (1)

typedef struct _STUDENT {
	int a;	// 0 ~ 3	//0~3
	char b; //4 ~ 7		//4
	double c;//8 ~ 15	//5~12
	float d;// 16 ~ 23	//13~16
}Student;

void test01()
{
	printf("student sizeof = %d\n", sizeof(Student));
}

当嵌套结构体时,以子结构体最大数据类型的整数倍来取地址即可

typedef struct _STUDENT2 {
	char a;		//0 ~ 7
	Student b;	//8 ~ 31
	double c;	//32 ~ 39
}Student2;

void test02()
{
	printf("student sizeof = %d\n", sizeof(Student2));
}

文件操作

文件的概念

通过fopen打开文件,中间还有一步open系统函数,open系统函数获取文件后,返回文件的指针FILE*给fopen

在这里插入图片描述

流的概念

C语言中,I/O操作可以简单地看做从程序移进或移出字节,这种搬运的过程称为流。I/O都是相对于程序来说,所以o输出为将字节输出到文件里,I输入为从文件读取

文本流

二进制流

我们程序中,经常看到的文本方式打开文件和二进制打开文件仅仅体现在换行符的处理上

输入/输出函数家族

家族名 目的 可用于所有流 只用于stdin和stdout
getchar 字符输入 fgetc、getc getchar
putchar 字符输出 fputc、putc putchar
gets 文本行输入 fgets gets
puts 文本行输出 fputs puts
scanf 格式化输入 fscanf scanf
printf 格式化输出 fprintf pirntf

文件缓冲区

有了缓冲区:提高硬盘寿命、提高运行效率
在这里插入图片描述
写文件时需要写fclose,因为可能有些数据留在写文件缓冲区,要是直接结束,可能会留这部分数据在里面没写进去,需要我们手动将其输出到文件

文件打开关闭

“r” 读
“w”写
“a”追加
“rb”二进制只读
“wb”二进制只写
“ab”二进制追加
“r+”允许读和写,文件必须已存在
“w+”允许读和写,如果文件不存在则创建,已存在则把文件长度截断为0字节再重新写
“a+”允许读和追加数据,如果文件不存在则创建
“rb+”以读或写方式打开一个二进制文件
“wb+”以读或写方式建立一个新的二进制文件
“ab+”以读或写方式打开一个二进制文件进行追加

文件关闭fclose

对打开文件进行写入时,若文件缓冲区的空间未被写入的内容填满,这些内容弄不会写到打开的文件中。只有对打开的文件进行关闭操作时,停留在文件缓冲区的内容才能写到改文件中,从而使文件完整。再者一旦关闭了文件,该文件对应的FILE结构将被释放,从而使关闭的文件受到保护,因为这时对该文件的存取操作将不会进行。文件的关闭也意味着释放了该文件的缓冲区

文件读写函数回顾

  • 按照字符读写文件:fgetc(),fputc()
  • 按照行读写文件:fputs(),fgets()
  • 按照块读写文件:fread(),fwrite()
  • 按照格式化读写文件:fprintf(),fscanf()
  • 按照随机位置读写文件:fseek(),ftell(),rewind()

while ((ch = fgetc(f_read)) != EOF)
判断是否是文件尾

//按字符方式读写
void test01()
{
	//写文件
	FILE * f_write = fopen("./test1.txt", "w+");
	
	if (f_write == NULL)
	{
		return;
	}
	
	char buf[] = "hello world";

	for (int i = 0; i < strlen(buf); i++)
	{
		fputc(buf[i], f_write);
	}

	fclose(f_write);

	//读文件
	FILE* f_read = fopen("./test1.txt", "r");

	if (f_read == NULL)
	{
		return;
	}
	char ch;
	while ((ch = fgetc(f_read)) != EOF)
	{
		printf("%c", ch);
	}
	printf("\n");

	fclose(f_read);
}

按格式化读写文件

//移动文件光标
void test05()
{
	//打开文件
	FILE* f_write = fopen("./test5.txt", "wb");
	if (f_write == NULL)
	{
		return;
	}

	struct Hero heros[4] =
	{
		{"geats",19},
		{"revice",20},
		{"saber",21},
		{"zero one",22}
	};

	for (int i = 0; i < 4; i++)
		//参数1 写入数据的地址  参数2 块大小  参数3 快个数  参数4 文件指针
		fwrite(&heros[i], sizeof(struct Hero), 1, f_write);

	//关闭文件
	fclose(f_write);


	//读文件
	FILE* f_read = fopen("./test5.txt", "rb");
	struct Hero temp;

	if (f_read == NULL)
	{
		perror("文件打开失败");//errno宏 全局变量
		return;
	}
	//移动文件光标
	//fseek(f_read, sizeof(struct Hero) , SEEK_SET);
	fseek(f_read, -(long)sizeof(struct Hero) * 2, SEEK_END);
	
	//将文件光标置首
	rewind(f_read);

	fread(&temp, sizeof(struct Hero), 1, f_read);

	printf("姓名: %s 年龄:%d\n", temp.name, temp.age);

	fclose(f_read);
}

//参数1 写入数据的地址 参数2 块大小 参数3 快个数 参数4 文件指针
fwrite(&heros[i], sizeof(struct Hero), 1, f_write);

//移动文件光标
//fseek(f_read, sizeof(struct Hero) , SEEK_SET);
fseek(f_read, -(long)sizeof(struct Hero) * 2, SEEK_END);
前者从前往后移,后者从后往前移,后者需要强制类型转换

//将文件光标置首
rewind(f_read);

  perror("文件打开失败");//errno宏 全局变量
对于每一个错误都会有一个相应的代码

文件读写注意事项

void test01()
{
	//按照字符读test文件

	FILE* file = fopen("test.txt", "r");

	if (file == NULL)
		return;

#if 0
	char  ch;
	while (!feof(file))
	{
		ch = fgetc(file);
		if (feof(file))
		{
			break;
		}
		printf("%c", ch);
	}
#endif

	char ch;
	while ((ch = fgetc(file)) != EOF)
	{
		printf("%c", ch);
	}
	fclose(file);
}
  • EOF为结尾,在if内部的代码用feof有滞后性,在文件读取结束后,还会进一次循环后再退出,if下面的代码就可以解决

  • 如果结构体中属性,创建在堆区,保存数据到文件中的时候,要将指针放入到文件中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Delphi 中 FireDAC 数据库连接(定义连接)

一、定义连接&#xff08;FireDAC&#xff09;概述连接定义是一组参数&#xff0c;它定义了如何使用特定的FireDAC驱动将一个应用程序连接到DBMS。它相当于一个BDE别名、ADO UDL&#xff08;存储的OLEDB连接字符串&#xff09;或ODBC数据源名称&#xff08;DSN&#xff09;。关…

Vue下载安装步骤的详细教程(亲测有效) 1

目录 一、【准备工作】nodejs下载安装(npm环境) 1 下载安装nodejs 2 查看环境变量是否添加成功 3、验证是否安装成功 4、修改模块下载位置 &#xff08;1&#xff09;查看npm默认存放位置 &#xff08;2&#xff09;在 nodejs 安装目录下&#xff0c;创建 “node_global…

Spring中AOP的使用以及举例

1.AOP的简介 1.1 什么是AOP? AOP(Aspect Oriented Programming)面向切面编程&#xff0c;一种编程范式&#xff0c;指导开发者如何组织程序结构。 OOP(Object Oriented Programming)面向对象编程 我们都知道OOP是一种编程思想&#xff0c;那么AOP也是一种编程思想&#xf…

模块电源DC/DC直流隔离升压稳压HRB系列5v12v24v转50v100v110v200v220v250v300v400v

特点效率高达80%以上1*1英寸标准封装单电压输出稳压输出工作温度: -40℃~85℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好可直接焊在PCB 上应用HRB 0.2~10W 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#xff1a;4.5~9V、9~18V、及18~36VDC标准&#…

中小企业数字化自动化转型的方法

自动化是我们国内未来的趋势。智能制造的实现主要依托两个基础能力&#xff0c;一个是工业制造技术&#xff0c;另一个就是工业互联网。而自动化是工业制造技术的重要组成部分&#xff0c;是高度智能制造装备的核心部分&#xff0c;与承接着制造单元与工业互联网这两大核心。懂…

Polkadot 基础

Polkadot Polkadot联合并保护了一个不断增长的专业区块链生态系统&#xff0c;称为parachains。Polkadot上的应用程序和服务可以安全地跨链通信&#xff0c;形成真正可互操作的去中心化网络的基础。 真正的互操作性 Polkadot支持跨区块链传输任何类型的数据或资产&#xff0c;…

基于51单片机和proteus的智能调速风扇设计

此智能风扇是基于51单片机和proteus的仿真设计&#xff0c;功能如下&#xff1a; 1. Timer0 PWM控制电机转速 2. DHT11采集温湿度 3. LCD1602显示温湿度及电机状态 4. 按键控制电机加减速启停等 5. 串口控制电机加减速启停等 功能框图如下&#xff1a; Proteus仿真界面如下…

EasyExcel使用与详细说明,EasyExcel工具类

文章目录1.Apache POI1.1 学习使用成本较高1.2 POI的内存消耗较大1.3 特点2. 初识EasyExcel2.1 重写了POI对07版Excel的解析2.2 特点3.快速入门3.1 导入依赖坐标3.2 最简单的读3.2.1 需求、准备工作3.2.2 编写导出数据的实体3.2.3 读取Excel的监听器&#xff0c;用于处理读取产…

rabbitmq在linux系统下安装步骤

第一步&#xff1a;登录官网 官网地址&#xff1a;www.rabbitmq.com,点击Get Started 重要信息&#xff1a;RabbitMQ Tutorials手册&#xff0c;描述了工作模式 第二步&#xff1a;点击Download Installation下载 重要信息&#xff1a;rabbitmq是用erlang语言开发的&#xff0…

C++类与对象(上)【详析】

目录1.面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符及封装4.1访问限定符4.2封装5.类的作用域6.类的实例化7.类对象模型7.1 如何计算类对象的大小8.this关键字如果说我们对C的初步认识&#xff0c;是觉得C是对C语言不足之处的进行修补&#xff0c;在认识完类…

Vue-router 3.x 版本中路由守卫钩子函数解析

目录概念&#xff1a;分类全局前置守卫 &#xff08; router.beforeEach &#xff09;全局解析守卫 &#xff08; router.beforeResolve &#xff09;全局后置守卫 &#xff08; router.afterEach &#xff09;路由独享守卫 &#xff08; beforeEnter &#xff09;组件内的守卫…

有哪些办法可以降低 Redis 的内存使用情况

在逛知乎时&#xff0c;看到这样一个问题&#xff0c;觉得挺不错的&#xff0c;将自己个人的见解分享给大家。问题是:有哪些办法可以降低 Redis 的内存使用情况&#xff1f;个人也对Redis做了一个比较全面的问题汇总&#xff0c;希望对大家有所帮助。Redis面试题汇总要降低内存…

Git同时配置Github和Gitlab

电脑的git需要同时管理Github上自己的代码仓库和Gitlab的公司的代码仓库&#xff0c;所以记录同时配置两者的步骤。 第一步、清除已有的全局配置&#xff08;我之前只有github的配置&#xff09; git config --global --unset user.name git config --global --unset user.em…

【论文阅读总结】Mask R-CNN翻译总结

Mask R-CNN1.摘要Mask R-CNN相关介绍与优点2.引言3.文献综述3.1 R-CNN3.2 Instance Segmentation【实例分割】4. Mask R-CNN介绍4.1 Faster R-CNN(相关细节请看相关文章)4.2 Mask R-CNN4.3 Mask Representation【遮罩表示法】4.4 RoIAlign【感兴趣区域对齐】4.4.1 RoIPool【感兴…

软件测试面试问答

笔试 笔试的话我们需要揣测具体会考什么内容&#xff0c;我们可以通过招聘信息去了解该公司需要什么样的技能&#xff0c;以此来准备笔试。一般必考的内容会有编程&#xff0c;测试用例设计&#xff0c;工作流程&#xff0c;逻辑思维等内容&#xff0c;除此之外每个公司可能还会…

【设计模式】我终于读懂了模板方法模式。。。

&#x1f34e;豆浆制作问题 编写制作豆浆的程序&#xff0c;说明如下: 1.制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎 2)通过添加不同的配料&#xff0c;可以制作出不同口味的豆浆 3)选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样…

二、IA-32系列处理器 通用寄存器介绍

IA-32系列处理器 通用寄存器介绍 寄存器处理器内的特殊存储单元处理器内有多种不同用途的寄存器寄存器分别有各自的名称,以便表示及访问通用寄存器IA-32系列CPU有8个32位的通用寄存器(General-Purpose Registers)通用寄存器不仅能存储数据,而且能参与算术逻辑运算,还能给出…

OpenCV 图像轮廓检测

本文是OpenCV图像视觉入门之路的第15篇文章&#xff0c;本文详细的介绍了图像轮廓检测的各种操作&#xff0c;例如&#xff1a;轮廓检索模式、轮廓逼近算子等操作。 图像轮廓是具有相同颜色或灰度的连续点的曲线&#xff0c;轮廓在形状分析和物体的检测和识别中很有用。图像轮廓…

预训练BERT

与PTB数据集相比&#xff0c;WikiText-2数据集保留了原来的标点符号、大小写和数字&#xff0c;并且比PTB数据集大了两倍多。 我们可以任意访问从WikiText-2语料库中的一对句子生成的预训练&#xff08;遮蔽语言模型和下一句预测&#xff09;样本。 原始的BERT有两个版本&…

《Python机器学习》基础代码

1&#xff0c;要学习Python机器学习,第一步就是读入数据,这里我们以读入excel的数据为例,利用jupyter notebook来编码,具体教程看这个视频 推荐先上传到jupyter notebook,再用名字.xlsx来导入 Jupyter notebook导入Excel数据的两种方法介绍_哔哩哔哩_bilibili 2&#xff0c;…