通讯录优化(动态内存管理+文件操作详解)

-----------------------------------纸上得来终觉浅,绝知此事要躬行-----------------------------------

1.前言

本篇是在通讯录(C语言实现)-CSDN博客基础上增加了动态内存存储以及文件写入保存与读取的优化。

1.1 动态内存管理

malloc(unsigned int size[申请空间大小]):用于申请一片连续的内存空间,返回值是所申请内存空间的首地址,申请不成功返回NULL。

calloc(unsigned int num[申请空间个数],unsigned int size[每个空间所占大小]):和malloc类似,与malloc不同的是calloc在申请一片连续的内存空间后并对这篇内存空间进行初始化为“0”,返回值同样是所申请内存空间首地址,申请不成功返回NULL。

realloc(void *mem_address[需要改变内存首地址], unsigned int newsize[新内存大小]):改变空间大小,用于增加或者减小内存空间,realloc在执行过程中是在在内存中找一片和改变的新内存大小一样大且连续的内存空间,可能地址与需要改变的首地址相同也可能不同,然后将内容给复制粘贴到新地址的一片空间,返回值同样是所申请内存空间首地址,申请不成功返回NULL。

1.2 文件操作

1.2.1文件的打开

FILE * fopen ( const char * filename, const char * mode );

参数介绍:
const char * filename:为要打开文件的相对地址或者绝对地址。

const char * mode:文件指针。

返回值:

打开成功返回文件首地址,失败返回NULL。

文件的打开方式:

文件使用方式含义如果指定的文件不在
“ r ” (只读)为了输入数据,打开一个已经存在的文本文件出错
“ w ” (只写)为了输出数据,打开一个文本文件建立一个新文件
“ a ” (追加)向文本文件尾部添加数据建立一个新文件
“ rb ”(只读)为了输入数据,打开一个二进制文件出错
“ wb ”(只写)为了输出数据,打开一个二进制文件建立一个新文件
“ ab ”(追加)向二进制文件尾部添加数据出错
“ r+ ”(读写)为了读和写,打开一个文本文件出错
“ w+ ”(读写)为了读和写,建立一个新的文件建立一个新文件
“ a+ ”(读写)打开一个文件,在文件尾部进行读写建立一个新文件
“ rb+ ”(读写)为了读和写,打开一个二进制文件出错
“ wb+ ”(读写)为了读和写,新建一个新的二进制文件建立一个新文件
“ ab+ ”(读写)打开一个二进制文件,在文件尾部进行读和写建立一个新文件

1.3 文件的顺序读写

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输入流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输入流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输入流
二进制输入fread文件流
二进制输出fwrite文件流

1.3.1 fgetc函数

int fgetc ( FILE * stream );

功能:读取文件单个字符。
参数介绍:

stream 为指向想要读取字符的文件的地址的指针。

返回值:

读取的字符的ASCII值,读取失败返回EOF(-1)。

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	//读文件
	char ch;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}
	fclose(pf);
    printf("\n")
	pf = NULL;
}

1.3.2 fputc函数

int fputc ( int character , FILE * stream );

功能:往文件写入单个字符。

参数介绍:

int character:为想要写入的单个字符。

FILE * stream:为指向想要读取字符的文件的地址的指针。

返回值:

读取的字符的ASCII值,读取失败返回EOF(-1)。

int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
	printf("%s\n", strerror(errno));
}
//写文件
fputc('a', pf);
fclose(pf);
pf = NULL;
}

1.3.3 fgets函数

char * fgets ( char * str, int num, FILE * stream );

功能:读取文件中一串字符

参数介绍:

char * str:从文件中读取的字符串的接受数组首地址或者指针;

int num:为读取字符串的个数;

FILE * stream:为指向想要读取文件的地址的指针。

返回值:

读取成功返回字符串首地址,读取失败返回NULL。

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen::");
	}
	char arr[20];
	fgets(arr,20, pf);
	printf("%s", arr);
	fclose(pf);
	pf = NULL;
}

 

1.3.4 fputs函数

int fputs ( const char * str, FILE * stream );

功能:写入文件一串字符。

参数介绍:

const char * str:为想要写入的字符串;

FILE * stream:为指向写入字符串的文件的地址的指针。

返回值:

成功返回写入字符个数,失败返回EOF(-1)。

int main()
{
	FILE* pf = fopen("text.txt", "a");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	//写一行
	fputs("hello world\n", pf);
	fclose(pf);
	pf = NULL;
}

 

1.3.5 fscanf函数

int fscanf ( FILE * stream, const char * format, ... );
功能:

格式化的方式读取文件中的数据。
参数介绍:

FILE * stream:为指向写入格式化数据的文件的地址的指针;

const char * format:为写入数据的格式(例:%s、%d……);

返回值:

成功返回从文件读取的字符个数,失败返回EOF(-1)。

int main()
{

	struct s s1;
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen::");
	}

	fscanf(pf, "%d %s", &s1.a, s1.arr);
	printf("%d %s", s1.a, s1.arr);

	fclose(pf);
	pf = NULL;
}

 

1.3.6 fprintf函数

int fprintf ( FILE * stream, const char * format, ... );
功能:

将格式化变量的数据写入文件。
参数介绍:

FILE * stream:为指向写入格式化数据的文件的地址的指针;

const char * format:为写入数据的格式(例:%s、%d……)。

返回值:

成功返回写入字符的个数,失败返回EOF(-1)。

int main()
{

	struct s s1 = { 1,"abcdef" };
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen::");
	}
	fprintf(pf, "%d %s", s1.a, s1.arr);
	fclose(pf);
	pf = NULL;
}

 

1.3.7 fread函数

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
功能:

对文件进行二进制的读取数据。
参数介绍:

void * ptr:为指向存储要读取到文件的数据的指针;

size_t size:为‎要读取的数据的总大小(单位:字节);

size_t count:为最多读几个 size 大小为单位的数据(单位:字节);

FILE * stream:为指向读取数据的文件的地址的指针。

返回值:

成功返回读入字符的个数,失败返回EOF(-1)。

int main()
{

	struct s s1=;
	FILE* pf = fopen("text.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
	}
	//二进制读取
	fread(&s1, sizeof(struct s), 1, pf);
	printf("%d %s", s1.a, s1.arr);

	fclose(pf);
	pf = NULL;
}

1.3.8 fwrite函数

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
功能:

对文件进行二进制的写入数据。
参数介绍:

const void * ptr:为指向存储要写入到文件的数据的指针;

size_t size:为‎要写入的数据的总大小(单位:字节);

size_t count为最多写几个 size 大小为单位的数据(单位:字节);

FILE * stream:为指向写入数据的文件的地址的指针。
返回值:

成功返回写入字符的个数,失败返回EOF(-1)。

int main()
{

	struct s s1={129,"abcdef"};
	FILE* pf = fopen("text.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen::");
	}
	//二进制写入
	fwrite(&s1, sizeof(struct s),1, pf);
	fclose(pf);
	pf = NULL;
}

1.3.9 sprintf 函数 和 sscanf 函数

int sprintf ( char * str, const char * format, ... );

功能:

把一个格式化的数据写到字符串中,本质是把一个格式化的数据换成字符串

参数介绍:

char * str:为指向将格式化数据写入到字符串的地址的指针;

const char * format为写入数据的格式(例:%s、%d……)。

返回值:

成功返回写入字符串长度,失败返回一个负数。

int sscanf ( const char * s, const char * format, ...);

功能:

把一个字符串读出一个格式化的数据。
参数介绍:

const char * s:为指向存储字符串的地址的指针;

const char * format:为写入数据的格式(例:%s、%d……)。

返回值:

成功返回读入字符串长度,失败返回一个负数。

int main()
{
	struct s s1={1,"wangzhengfeng"};
	struct s s2 = { 0 };
	char buf[50]={0};
	sprintf(buf, "%d %s", s1.a, s1.arr); //s1-->buf
	printf("%s\n", buf);
	sscanf(buf, "%d %s", &s2.a, s2.arr);//buf-->s2
	printf("%s %d\n", s2.arr,s2.a);

}

1.4 文件的关闭

int fclose ( FILE * stream );

参数介绍:

FILE * stream:FILE*的文件指针

返回值:

关闭成功返回0,失败返回EOF(-1)。

1.5 文件的随机存取

1.5.1 fseek函数

int fseek ( FILE * stream, long int offset, int origin );
功能:

根据文件指针的位置和偏移量来定位文件指针。
参数介绍:

FILE * stream:为指向文件的地址的指针;

long int offset:为文件指针的偏移量;

int origin:为文件指针偏移量的相对起始地址。

int main()
{
	FILE* pf = fopen("text.txt", "r");


	///feek
	int n=ftell(pf);
	printf("%d", n);
	fseek(pf, 2, SEEK_SET);//起始偏移2字节
	char ch=getc(pf);
	printf("%c", ch);


	fseek(pf, 2, SEEK_CUR);//指针所在位置偏移两字节
	ch=getc(pf);
	printf("%c", ch);
	n=ftell(pf);
	printf("%d", n);

	fseek(pf, -1, SEEK_END);//末尾
	ch = getc(pf);
	printf("%c", ch);

	fclose(pf);
	pf = NULL;
}

1.5.2 ftell函数

long int ftell ( FILE * stream );

功能:

返回文件指针相对于起始位置的偏移量。

参数介绍:

FILE * stream:为指向文件的地址的指针。

返回值:

相对起始位置偏移量,起始位置偏移量为0。

 1.5.3  rwind函数

void rewind ( FILE * stream );

功能:

让文件指针的位置回到文件的起始位置。

参数介绍:

FILE * stream :为指向文件的地址的指针。

 2.动态内存管理版本

在原基础上的改动。

1.结构体

typedef struct Contact
{
	Infopeople *data;
	int conunt;
	int capacity;
}Contact;

2.初始化

int InitContact(Contact* c) {
	c->conunt = 0;
	c->data = (Infopeople*)calloc(DEFAULTVALUE, sizeof(Infopeople));
	if (NULL == c->data)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	c->capacity = DEFAULTVALUE;

	return 0;
}

3. 添加联系人

void Addpeople(Contact * c)
{
	Inc_Capatity(c);
	printf("请输入姓名:>\n");
	scanf("%s", c->data[c->conunt].name);
	printf("请输入年龄:>\n");
	scanf("%d", &(c->data[c->conunt].age));
	printf("请输入性别:>\n");
	scanf("%s", c->data[c->conunt].sex);
	printf("请输入电话:>\n");
	scanf("%s", c->data[c->conunt].telephone);
	printf("请输入地址:>\n");
	scanf("%s", c->data[c->conunt].address);
	printf("增加成功\n");
	c->conunt++;
}

 4.增加容量

void Inc_Capatity(Contact* c)
{
	if (c->conunt == c->capacity)
	{
		Infopeople* ptr = (Infopeople*)realloc(c->data, (INC_CAP + c->capacity) * sizeof(Infopeople));
		if (ptr == NULL)
		{
			printf("Addpeople::%s\n", strerror(errno));
			return;
		}
		c->data = ptr;
		c->capacity += INC_CAP;
		ptr = NULL;
		printf("增容成功\n");
	}
}

5.释放内存

void DestroyContact(Contact* c) {
	free(c->data);
	c->data = NULL;
	c = NULL;
}

 3.动态内存管理+文件操作版本

3.1 初始化

3.1.1 文本录入版本的初始化

int InitContact(Contact* c) {
	c->conunt = 0;
	c->data = (Infopeople*)calloc(DEFAULTVALUE, sizeof(Infopeople));
	if (NULL == c->data)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	c->capacity = DEFAULTVALUE-1;

	//加载联系人信息
	FILE* pfread = fopen("text.txt", "r");
	assert(pfread);
	if (NULL == pfread)
	{
		perror("InitContact");
		return;
	}
	int ret = 1;
	while (ret != -1)
	{
		Inc_Capatity(c);
		ret = fscanf(pfread, "%s %d %s %s %s",
			c->data[c->conunt].name,
			&(c->data[c->conunt].age),
			c->data[c->conunt].sex,
			c->data[c->conunt].telephone,
			c->data[c->conunt].address);
		if (ret != -1)

		{
			c->conunt++;

		}
	}
	
	fclose(pfread);
	pfread = NULL;
	return 0;
}
文本录入版本写入文件
void SaveContact(Contact* c)
{
	assert(c);
	FILE* pfwrite = fopen("text.txt", "w");
	if (NULL == pfwrite)
	{
		perror("SaveContact");
		return;
	}
	for (int i = 0; i < c->conunt; i++)
	{
		fprintf(pfwrite, "%s %d %s %s %s\n",
			c->data[i].name,
			c->data[i].age,
			c->data[i].sex,
			c->data[i].telephone,
			c->data[i].address);
	}

	fclose(pfwrite);
	pfwrite = NULL;
}

3.1.2 二进制录入版本

int InitContact(Contact* c) {
	c->conunt = 0;
	c->data = (Infopeople*)calloc(DEFAULTVALUE, sizeof(Infopeople));
	if (NULL == c->data)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	c->capacity = DEFAULTVALUE-1;

	//加载联系人信息
	//二进制文本录入
	FILE* pfread = fopen("text.txt", "rb");
	assert(pfread);
	if (NULL == pfread)
	{
		perror("InitContact");
		return;
	}
	Infopeople tmp = { 0 };
	while (fread(&tmp, sizeof(Infopeople), 1, pfread))
	{
		Inc_Capatity(c);
		c->data[c->conunt] = tmp;
		c->conunt++;
	}
	fclose(pfread);
	pfread = NULL;
	return 0;
}
 二进制录入版本写入文件
void SaveContact(Contact* c)
{
	assert(c);
	//二进制版本
	FILE* pfwrite = fopen("text.txt", "wb");
	if (NULL == pfwrite)
	{
		perror("SaveContact");
		return;
	}
	for (int i = 0; i < c->conunt; i++)
	{
		fwrite((c->data) + i, 1, sizeof(Infopeople), pfwrite);
	}
	fclose(pfwrite);
	pfwrite = NULL;
}

 4.结语

在动态内存空间的开辟和文件操作中存在诸多细节,比如我在写文件基于动态内存的文件操作通讯录的时候在每次开辟新空间开辟到4个的时候,第五个就会出现卡死现象,导致输入文件内的内容全部乱码,最后发现是初始化的时候需要先判断预置的空间是否能存储文件中读入的内存,如果不够则需要先开辟新的内存供给存储。

感谢大家观看,一起进步~~~~😆😆😆

 

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值