C++笔记day20 预处理 静态库 动态库 接口实现

92 阅读6分钟

链表反转作业

利用3个辅助指针,实现链表反转
提供返回链表节点个数 函数

示例

reverseLinkList(struct LinkNode* pHeader) //反转链表
{
	if (pHeader == NULL) 
	{
		return;
	}
	struct LinkNode* pPre = NULL;
	struct LinkNode* pCurrent = pHeader->next;
	struct LinkNode* nextNode =NULL;
	while (pCurrent != NULL) 
	{
		nextNode = pCurrent->next;
		pCurrent->next =pPre;
		pPre = pCurrent;
		pCurrent =nextNode;
	}
	pHeader->next = pPre;
}

int sizeLinkList(struct LinkNode* pHeader) //返回链表节点个数
{
	if (pHeader == NULL) 
	{
		return -1;
	}

	struct LinkNode* pCurrent = pHeader->next;
	int num = 0;
	while (pCurrent != NULL) 
	{
		num++;
		pCurrent = pCurrent->next;

	}
	return num;
}

回调函数作业-实现对任意数据类型数组进行排序

利用选择排序实现大框架

对比功能交还给用户指定

利用回调函数技术来实现对比

示例

void selectSort(void * pAddr,int eleSize,int len,int (*myCompare)(void *,void *)) 
{
	char* temp = malloc(eleSize); //循环中开辟的数据可以放在上面,释放一次就好了
	for (size_t i = 0; i < len; i++)
	{
		int minOrMax = i;
		for (size_t j = i+1; j < len; j++)
		{
			//定义出j元素的地址
			char* pJ =(char*)pAddr + eleSize * j;
			char* pMinOrMax = (char*)pAddr + eleSize * minOrMax;
			//if(pAddr[j]<pAddr[minOrMax]) 
			if (myCompare(pJ,pMinOrMax)) 
			{
				minOrMax = j;//更新最小值或最大值下标
			}
		}
		if (i != minOrMax) 
		{
			//交换i和minOrMin 下标元素
			char* pI = (char*)pAddr + i * eleSize;
			char* pMinOrMax = (char*)pAddr + eleSize * minOrMax;

			memcpy(temp, pI, eleSize);
			memcpy(pI, pMinOrMax, eleSize);
			memcpy(pMinOrMax, temp, eleSize);
		}
	}
	if (temp != NULL) 
	{
		free(temp);
		temp = NULL;
	}
}

int myCompareInt(void * data1,void *data2)
{
	int* num1 = data1;
	int* num2 = data2;

	if (*num1 > *num2)//从大到小排序
	{	
		return 1;
	}
	return 0;

}

void test01() 
{
	int arr[] = { 10,30,20,40,50,60 };
	int len = sizeof(arr) / sizeof(int);
	selectSort(arr, sizeof(int), len, myCompareInt);

	for (size_t i = 0; i < len; i++)
	{
		printf("%d\n", arr[i]);
	}

}

struct Person 
{
	char name[64];
	int age;
};

int myCompareStruct(void* data1, void* data2) 
{
	struct Person* p1 = data1;
	struct Person* p2 = data2;

	if (p1->age < p2->age) 
	{
		return 1;
	}
	return 0;
}

void test02() 
{
	struct Person pArray[] =
	{
		{"aaa",10},{"bbb",30},{"ccc",20},{"ddd",40}
	};

	int len = sizeof(pArray) / sizeof(struct Person);
	selectSort(pArray, sizeof (struct Person), len, myCompareStruct);

	for (size_t i = 0; i < len; i++)
	{
		printf("name = %s age = %d\n”, pArray[i].name, pArray[i].age);
	}
}

int main(void)
{
	test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

预处理指令

头文件  #include

<>  “”区别

    <> 包含系统头

    “” 包含自定义头

宏常量

    不重视作用域

    没有数据类型

    利用 #undef 卸载宏

宏函数

    将频繁、短小函数写成宏函数

    优点:以空间换时间

条件编译

    #ifdef   #else   #endif  测试存在

    #ifndef  #else   #endif   测试不存在

    #if    #else   #endif    自定义条件编译

特殊宏

__FILE__  宏所在文件路径

__LINE__  宏所在行

__DATE__ 宏编译日期  
 
__TIME__ 宏编译时间

示例

//1. 头文件包含 “”  <> 的区别
	// “”包含自定义头文件
	//<>包含系统头文件

//2. 宏 常量: 不重视作用域的
# define MAX 1024
//利用 #undef 卸载宏
//宏 常量 没有数据类型的

//宏 函数
//将短小、频繁使用的函数写成宏函数:优点 以空间换时间
//加括号保证运算完整性

//3. 条件编译
#define DEBUG

#ifdef DEBUG
void func() 
{
	printf("DEBUG版本发布\n");
}
#else
void func() 
{
	printf("Release版本发布\n");
}
#endif
//自定义条件编译
#if 0
#else
#endif

//特殊 宏 __FILE__  __LINE__  __DATE__ __TIME__等
void doWork(char* p) 
{
	if (p == NULL) 
	{
		printf("文件:%s 第 %d 行出错了\n", __FILE__, __LINE__);
		printf("日期: %s\n", __DATE__);
		printf("时间:%s\n", __TIME__);
	}
	return;
}

void test02() 
{
	doWork(NULL);
}

int main(void)
{
	printf("MAX = %d\n", MAX);
	func();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

静态库配置

右键项目->属性 ->常规->配置类型 ->静态库

生成项目   生成.lib文件

将.lib和 .h交给用户

测试

image.png

动态库配置

静态库优缺点

1. 静态库对库函数的链接是放在编译时期完成的,静态库在程序的链接阶段被复制到了程序中,跟程序的运行没有任何关系。

好处 在于二者之间没有关联了,程序在运行时与函数库再无瓜葛,移植方便。

坏处1 在于浪费空间和资源,所有相关的目标文件与牵扯到的函数库被链接合成一个可执行文件。

坏处2 静态链接对程序的更新、部署也比较麻烦。

动态库

针对空间浪费和更新困难两个问题,最简单的办法就是把程序的模块相互分隔开来,形成独立的文件,
把链接过程推迟到了运行时再进行。 

  ====================================================
1. 右键项目->属性 ->常规->配置类型 ->动态库 .dll

2. 生成项目  生成 .lib  .dll

    静态库中生成的.lib和动态库生成的.lib是不同的。
    动态库中的.lib只会放变量的声明和 导出函数的声明,函数实现体放在.dll中

 3. 导出函数/外部函数 : __declspec(dllexport)int mySub(int a, int b);

4. 测试

#pragma  comment( lib,"./mydll.lib")

image.png

递归函数

普通函数调用流程

image.png

image.png

递归函数调用流程

image.png

image.png

本质:函数自身调用自身
注意事项:递归函数必须有结束条件,函数有出口

案例1 利用递归实现字符串逆序遍历

image.png

//利用递归实现字符串逆序遍历
void reversePrint(char *p) 
{
	if (*p == '\0') 
	{
		return;
	}
	reversePrint(p + 1);
	printf("%c\n", *p);
}

void test01() 
{
	char* str = "abcdefg";
	reversePrint(str);
}

int main(void)
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

案例2 斐波那契数列

int fibonacci(int pos) 
{
	if (pos == 1 || pos == 2) 
	{
		return 1;
	}
	return fibonacci(pos - 1) + fibonacci(pos - 2);
}

void test02() 
{
	//斐波那契数列
	// 1 1 2 3 5 8 13 ....
	printf("第10位数字为多少 %d\n", fibonacci(10));
}

面向接口封装案例

案例背景

一般的企业信息系统都有成熟的框架。软件框架一般不发生变化,能自由的集成第三方厂商的产品。

案例需求

要求在企业信息系统框架中集成第三方厂商的游戏功能产品。软件设计要求:能够满足用户需求,完成的产品可以与用户完美对接。

案例要求

   1)能支持多个厂商的游戏功能产品入围
   
   2)能够实现第三方产品和用户产品的对接
   
   3)系统整体框架不轻易发生改变

编程提示

1)抽象游戏中玩家结构体设计(struct Player)

2)框架接口设计(playGame)

       a) 初始化游戏
       b) 核心功能战斗
       c) 查看玩家信息
       d) 结束游戏

3)   a) 游戏厂商1入围(GameCompany1)
      b) 游戏厂商2入围(GameCompany2)
4)框架接口分文件编写

案例实现

1. 甲乙两方设计接口
2. 甲方实现代码
//初始化游戏  参数1 玩家指针  参数2 玩家姓名
typedef void(*INIT_GAME)(void** player, char* name);

//副本战斗 返回战斗是否胜利  1.代表胜利 0.代表失败  参数1 玩家 参数2 游戏副本难度
typedef int(*FIGHT_GAME)(void* player, int gameDiff);
//查看玩家信息
typedef void(*PRINT_GAME)(void* player);
//离开游戏
typedef void(*EXIT_GAME)(void* player);


void playerGame(INIT_GAME init, FIGHT_GAME fight, PRINT_GAME printGame, EXIT_GAME exitGame)
{
	//初始化游戏
	void* player = NULL;

	printf("请输入姓名 \n");
	char userName[64];
	scanf("%s", userName);
	
	init(&player, userName);

	//副本难度变量
	int diff = -1;

	printf("请选择游戏难度: \n");
	printf("1/简单\n");
	printf("2/中等\n");
	printf("3/困难\n");

	scanf("%d", &diff);

	//战斗
	int ret = fight(player, diff);
	if (ret == 0) 
	{
		printf("defeat\n");
	}
	else 
	{
		printf("victory,player info is as following: \n");
		printGame(player);
	}

	//关闭游戏
	exitGame(player);
}
int main(void)
{



	system("pause");
	return EXIT_SUCCESS;
}

乙方实现代码

乙方头文件内容
struct Player 
{
	char name[64];
	int level; //玩家等级
	int exp; //玩家经验
};


//初始化游戏  参数1 玩家指针  参数2 玩家姓名
void INIT_GAME_COM1(void** player, char* name);

//副本战斗 返回战斗是否胜利  1.代表胜利 0.代表失败  参数1 玩家 参数2 游戏副本难度
int FIGHT_GAME_COM1 (void* player, int gameDiff);
//查看玩家信息
void PRINT_GAME_COM1 (void* player);
//离开游戏
void EXIT_GAME_COM1 (void* player);

//判断游戏是否胜利 乙方自己设计的
int isWin(int winRate, int diff);
乙方实现
#include "gameCompany1.h"



//初始化游戏  参数1 玩家指针  参数2 玩家姓名
void INIT_GAME_COM1(void** player, char* name) 
{
	struct Player* player1 = malloc(sizeof(struct Player));

	if (player1 == NULL) 
	{
		printf("初始化失\n");
		return;
	}

	*player = player1;

	strcpy(player1->name, name);
	player1->level = 0;
	player1->exp = 0;
}

//副本战斗 返回战斗是否胜利  1.代表胜利 0.代表失败  参数1 玩家 参数2 游戏副本难度
int FIGHT_GAME_COM1(void* player, int gameDiff) 
{
	struct Player* player1 = player;
	int addExp = 0; //增加经验值
	switch (gameDiff) 
	{
	case 1:
		addExp = isWin(90, 1);
		break;
	case 2:
		addExp = isWin(50, 2);
		break;
	case 3:
		addExp = isWin(30, 3);
		break;
	default:
		break;
	}
	//累加经验到玩家身上
	player1->exp += addExp;
	player1->level = player1->exp / 10;

	if (addExp == 0)
	{	
		return 0;
	}
	else 
	{
		return 1;
	}
}

//判断游戏是否胜利 乙方自己设计的
int isWin(int winRate, int diff) 
{
	int random = rand() % 100 + 1;
	if (random <= winRate) 
	{
		return diff * 10; //返回经验 =游戏难度等级*10
	}
	else 
	{
		return 0;
	}
}


//查看玩家信息
void PRINT_GAME_COM1(void* player) 
{
	struct Player* player1 = player;
	printf("玩家<%s> -----当前等级:<%d>级----当前经验:<%d>\n", player1->name, player1->level, player1->exp);
}
//离开游戏
void EXIT_GAME_COM1(void* player) 
{
	if (player == NULL) 
	{
		return;
	}
	free(player);
	player == NULL;

}

接口对接

甲方加入乙方的头文件
int main(void)
{
	srand((unsigned int)time(NULL));
	playGame(INIT_GAME_COM1, FIGHT_GAME_COM1, PRINT_GAME_COM1, EXIT_GAME_COM1);

	system("pause");
	return EXIT_SUCCESS;
}