C语言-note004

1. 内存函数

1-1 memcpy        内存拷贝

        memcpy(参数1【目的】,参数2【源】,参数3【无符号整型】)

        memcpy函数不能用来处理重叠的内存空间的数据拷贝

                使用memmove实现重叠内存空间的数据拷贝

        void* memcpy(void* dest,const void* src,size_t num){ }

                void*:通用类型指针

1-2 memmove        内存拷贝

        分两种情况拷贝:

                (1)从前向后拷贝

                                dest < src

                (2)从后向前拷贝

                                dest > src

        示例:

                int arr[ ] = {1,2,3,4,5,6,7,8,9,10...};

                int arr1[ ] = {3,4,5,6,7};//src

                int dest1[ ] = {2,3,4,5,6};//dest < src,从前向后拷贝

                int dest2[ ] ={5,6,7,8,9};//dest > src,从后向前拷贝

1-3 memcmp        内存比较

        一个字节一个字节进行比较

 1-4 memset        内存设计

        memset(目的空间,设置的值,设置的值的字节数)

int arr[] = "hello,world!";
memset(arr, 'h', 5);    //从数组第一个元素开始,替换成h,共替换5个元素

printf("%s\n", arr);

替换后的数组:arr [ ] = “hhhhh,world!”;

int arr[] = "hello,world!";
memset(arr+5, 'x', 3);    //从第arr+5个元素开始,将元素替换成x,共替换3个元素

printf("%s\n", arr);

替换后的数组:arr[ ] = “hello,xxxld!”;

2. 自定义类型

2-1 结构体

        结构体:值的集合。

                  值被称为成员变量

        2-1-1 结构体的声明:

              struct 结构体标签

                {

                        成员变量;

                }变量列表;        //变量列表是:1. 利用结构体类型创建的变量;2. 结构体的全局变量

        结构体的自引用

                自己找到与自己同类型的另一个结点

struct node
{
    int data;
    struct node* next;
};

                       

        数据结构:描述数据在内存中的存储结构

              2-1-2  结构体变量的定义和初始化

                        struct point

                        {

                                int x;

                                int y;

                                

                        }p1 = (2,3);        //p1变量

                2-1-3 结构体内存对齐

                        计算结构体大小

                        (1)结构体对齐规则:

                                (1-1)第一个成员变量的对齐数

                                        放在结构体偏移量为0的地址处

                               (1-2) 其他成员变量对齐数

                                        对齐到某个数字(对齐数)的整数倍的地址处

                                                对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值

                                                         VS编译器默认的对齐数是8

                                  (1-3) 结构体的总大小

                                        最大对齐数的整数倍    

                                    (1-4)嵌套了结构体的情况

                                        嵌套的结构体要对齐到自己的最大对齐数的整数倍处

                                                 VS编译器默认的对齐数是8

                                        结构体的整体大小

                                                所有最大对齐数(含嵌套结构体的对齐数)的整数倍

                        (2)为什么存在内存对齐

                                (2-1)平台原因(移植原因)

                                              不是所有的硬件平台都能访问任意地址上的任意数据

                                                        某些硬件平台只能访问某些地址处某些特定类型的数据,否则抛出硬件异常 

                                 (2-2)性能原因

                                                数据结构(尤其是栈)应该尽可能的对齐

                                                对齐的内存访问仅需要一次访问

                                                        未对齐的内存需要两次内存访问

                                                总体来说,结构体的内存对齐是 空间换时间的做法

                                                设计结构体时,既要满足对齐,又要节省空间,如何做:

                                                         让占用空间小的成员变量尽量集中在一起

                                 (3)修改默认对齐数     

                                          #pragma pack(4)//改成4

                                          #pragma once        //防止头文件按被多次引用

2-2 结构体传参

//定义结构体
struct S
{
    int data[1000];
    int num;
};


//主函数
int main()
{
    struct S s = {(1,2,3),100};
    print1(s);
    print2(&s);
}

        传值调用:

void print1(sturuct S ss)
{
    int i = 0;
    for(i = 0; i <3; i++)
    {
        printf("%d ",ss.data[i]);
        
    }
    printf("%d\n",ss.num);
}

        传址调用:

void print1(sturuct S* ps)
{
    int i = 0;
    for(i = 0; i <3; i++)
    {
        printf("%d ",ps->data[i]);
        
    }
    printf("%d\n",ps->num);
}

优先选择传址调用

        原因:函数在传参时,会有函数压栈,会有时间和空间上的开销

3. 位段

        位:比特位

        通过结构体实现

        一种节省空间的做法

3-1 什么是位段

        (1)位段的声明和结构体类似,有2个不同

                位段的成员必须是int、unsigned int、signed int

                位段的成员后面有:一个冒号和一个数字

                        冒号后的数字不能超过成员大小的比特位

                                int:4字节(32个比特位)

                                char:1字节(8个比特位)

                       示例:

struct A
{
    int_a:2;
    int_b:5;
    int_c:10;
    int_d:30;
};

3-2 位段的内存分配

        位段的空间是根据需要按照4字节(int)或者1字节(char)开辟空间的

        位段不跨平台

                注重可移植的程序避免使用位段

        VS2019中,位段存放由低位到高位(右向左)进行存放

3-3 位段的跨平台问题

        int位段不确定被当成有符号数还是无符号数

        位段中最大位的数目不确定

        位段中的成员未定义在内存中是从左到右还是从右到左进行分配

       第二个位段比较大,无法容纳于第一个位段剩余的位置时,不确定是舍弃剩余的位还是利用

综上:位段不跨平台

4. 实现通讯录

        (1)存放人的信息        

                姓名

                年龄

                性别

                ...

        (2)增加存放的人数的信息

                增删查改

                排序

                显示联系人

                ...        

4-1. test.c        测试功能

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

menu()
{
	printf("**********************************************\n");
	printf("***  1.add              2.del     ************\n");
	printf("***  3.modify           4.search  ************\n");
	printf("***  5.show             6.sort    ************\n");
	printf("***  0.exit                       ************\n");
	printf("**********************************************\n");



}

int main()
{
	int input = 0;

	//将存放联系人的信息和记录联系人的信息封装到contact.h中
	//PeoInfo data[ MAX];//存放100个人的信息
	//int count = 0;//记录当前通讯录的联系人信息

	Contact con;//通讯录

	//初始化通讯录
	InitContact(&con);//传参,传地址


	do
	{
		

		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				printf("增加联系人:>\n");
				AddContact(&con);
				break;
			case 2:
				printf("删除联系人:>\n");
				DelContact(&con);
				break;
			case 3:
				printf("修改联系人:>\n");
				ModifyContact(&con);
				break;
			case 4:
				printf("查询联系人:>\n");
				SearchContact(&con);
				break;
			case 5:
				printf("显示联系人:>\n");
				ShowContact(&con);
				break;
			case 6:
				printf("联系人排序:>\n");
				SortContact(&con);
				break;
			case 0:
				printf("退出通讯录:>\n");
				break;
			default:
				printf("输入错误,请重新输入:>\n");
				break;
		}
	} while (input);

	return 0;
}

4-2. contact.c        通讯录相关的实现

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.h"

//实现函数
//初始化函数
void InitContact(Contact* pc)
{
	assert(pc);//断言一下pc不为空指针;需要引入头文件assert.h,引入到contact.h中
	pc->count = 0;//初始化通讯录为空
	memset(pc->data, 0, sizeof(pc->data));//memset函数需要引入头文件,引入到contact.h中
}

//增加联系人信息
void AddContact(Contact* pc)
{
	assert(pc);
	//判断通讯录是否满了
	if (pc->count == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;

	}
	//增加联系人信息
	printf("请输入姓名:>");
	scanf("%s", pc->data[pc->count].name);//name是数组名,数组名本身就是地址,不需要取地址&
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->count].age));//age是整型变量,需要取地址&
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->count].sex);
	printf("请输入电话;>");
	scanf("%s", pc->data[pc->count].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc -> count].addr);//pc指向的数组data;下标是pc指向的count:pc->count;成员里面的addr

	pc->count++;
	printf("联系人信息增加成功\n");
}


void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-5s\t%-10s\t%-15s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");//增加抬头信息。一个汉字至少2个字符,所以年龄对应的格式是%5s;‘-’号表示左对齐
	for (i = 0; i < pc ->count; i++)
	{
		printf("%-20s\t%-5d\t%-10s\t%-15s\t%-30s\n", pc->data[i].name,
			pc->data[i].age, pc->data[i].sex, 
			pc->data[i].tel, pc->data[i].addr);//\t是水平制表符,让他空开
	}
}




static int FindByName(Contact* pc,char name[])//static修饰,FindByName函数只能在这个文件中看到
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->count; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;
}

void DelContact(Contact* pc)
{
	char name[MAX_NAME] = { 0 };//需要删除的联系人的姓名
	assert(pc);
	int i = 0;
	if (pc->count == 0)
	{
		printf("通讯录为空,没有联系人可删除:\n");
		return;
	}
	printf("请输入要删除的联系人的姓名:>");
	scanf("%s",name );

	//删除,前提通讯录里不是空的,有联系人存在
	//1.查找。哪里都会用到查找这个操作,封装成函数
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("需要删除的联系人不存在\n");
		return;
	}
	//2.删除
	for (i = pos; i < pc->count-1; i++)
	{
		pc->data[i] = pc->data[i + 1];//覆盖,i+1覆盖i
	}
	pc->count--;
	printf("已成功删除联系人\n");
}



void SearchContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };//需要查找的联系人的姓名
	printf("请输入要查找的联系人的姓名:>");
	scanf("%s", name);
	//1.查找。哪里都会用到查找这个操作,封装成函数
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("需要查找的联系人不存在\n");
		return;
	}
	printf("%-20s\t%-5s\t%-10s\t%-15s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");//增加抬头信息。一个汉字至少2个字符,所以年龄对应的格式是%5s;‘-’号表示左对齐

		printf("%-20s\t%-5d\t%-10s\t%-15s\t%-30s\n", pc->data[pos].name,
			pc->data[pos].age, pc->data[pos].sex,
			pc->data[pos].tel, pc->data[pos].addr);//\t是水平制表符,让他空开
	
}



void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };//需要修改的联系人的姓名
	printf("请输入要修改的联系人的姓名:>");
	scanf("%s", name);
	//1.查找。哪里都会用到查找这个操作,封装成函数
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("需要修改的联系人不存在\n");
		return;
	}
	printf("需要修改的联系人已找到,请开始修改:>");
	//修改
	//重新录一遍信息
	printf("请输入姓名:>");
	scanf("%s", pc->data[pos].name);//name是数组名,数组名本身就是地址,不需要取地址&
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));//age是整型变量,需要取地址&
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入电话;>");
	scanf("%s", pc->data[pos].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);//pc指向的数组data;下标是pc指向的count:pc->count;成员里面的addr

	printf("已成功修改联系人信息\n");
}



int cam_peo_by_name(const void* e1,const void* e2)//按名字比较
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
	//按名字排序
	assert(pc);
	qsort(pc->data,
		pc->count,
		sizeof(PeoInfo),
		cam_peo_by_name
		);//4个参数:起始位置,元素个数(几个元素排序),需要排序的一个元素有多大,比较函数的函数指针(2个参数:指向要比较的2个元素的地址)

	printf("已成功排序联系人信息\n");
}

4-3. contact.h        通讯录相关的声明

#pragma once

#include <stdio.h>
#include <string.h>
#include <assert.h>

//符号定义
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TEL 15
#define MAX_ADDR 30

//声明类型
//将结构体people重命名为peoInfo
//联系人的信息
typedef struct People
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tel[MAX_TEL];
	char addr[MAX_ADDR];
}PeoInfo;


//重命名为Contact
//通讯录的信息
typedef struct Contact
{
	PeoInfo data[MAX];//存放100个人的信息
	int count;//记录当前通讯录的联系人信息
}Contact;


//声明函数
//初始化通讯录
void InitContact(Contact* pc);


//增加联系人的通讯录
void AddContact(Contact* pc);

//显示通讯录信息
void ShowContact(const Contact* pc);//只显示,不修改,加const保护,不能修改

//删除通讯录联系人
void DelContact(Contact* pc);

//查找联系人
void SearchContact(Contact* pc);
//修改联系人信息
void ModifyContact(Contact* pc);
//排序
void SortContact(Contact* pc);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值