并非从0开始的c++ day10
实现对任意类型数组的排序
这里使用选择排序的方式,将数组升序或降序排序
第一个为正常的int类型数组
int compareInt(void * data1,void *data2)
{
int* num1 = data1;
int* num2 = data2;
//升序
//if (*num1 < *num2)
//{
// return 1;
//}
//else
//{
// return 0;
//}
return *num1 < *num2;
}
void test01()
{
int arr[] = { 10,25,30,24,0,60,7,85,9 };
int len = sizeof(arr) / sizeof(int);
selectSort(arr, sizeof(int), len,compareInt);
for (int i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
}
第二个为自设的结构体类型数组
//结构体数组排序
struct Person
{
char name[64];
int age;
};
//按照年龄做降序
int comparePerson(void* data1, void* data2)
{
struct Person* p1 = data1;
struct Person* p2 = data2;
if (p1->age > p2->age)
{
return 1;
}
else
{
return 0;
}
}
void test02()
{
struct Person arr[] =
{
{"aaa",10},
{"bbb",20},
{"ccc",30},
{"ddd",40},
{"eee",50},
};
int len = sizeof(arr) / sizeof(struct Person);
selectSort(arr, sizeof(struct Person), len, comparePerson);
for (int i = 0; i < len; i++)
{
printf("名字为:%s 年龄为:%d\n", arr[i].name, arr[i].age);
}
}
同一用同一种算法来进行排序
void selectSort(void* arr, int eleSize, int len,int (*myCompare)(void *,void*))
{
for (int i = 0; i < len; i++)
{
//认定i就是最大值或者最小值
int minOrMax = i;
for (int j = i + 1; j < len; j++)
{
//获取j元素首地址
char* pJ = (char*)arr + eleSize * j;
//获取当前最小值或者最大值元素的首地址
char* pMinOrMax = (char*)arr + eleSize * minOrMax;
if (myCompare(pJ,pMinOrMax))
{
minOrMax = j;
}
}
if (i != minOrMax)//如果认定最大值或最小值 不是计算出的minOrMax
{
//通过内存拷贝来实现交换
char* pI = (char*)arr + i * eleSize;
char* pMinOrMax = (char*)arr + minOrMax * eleSize;
char* temp = malloc(eleSize);
memcpy(temp, pI, eleSize);
memcpy(pI, pMinOrMax, eleSize);
memcpy(pMinOrMax, temp, eleSize);
}
}
}
预处理
c语言对源程序处理的四个步骤:预处理、编译、汇编、链接
预处理是在程序源代码被编译之前,由预处理器(preprocessor)d对程序源代码进行的处理。这个过程并不对程序的源代码语法进行解析,但它会把源代码分割或处理称为特定的符号为下一步的编译做准备工作
文件包含指令(#include)
文件包含处理
文件包含处理 是指一个源文件可以将另外一个文件的全部内容包含进来。c语言提供了#include命令来实现“文件包含”的操作
#include<>和#include’'区别
“” 表示系统先在file1.c所在目录找file1.h,如果找不到再按照系统指定的目录检索
<> 表示系统直接按照系统指定的目录检索
#include<> 常用于包含库函数的头文件
#include"" 常用于包含自定义的头文件
理论上#include可以包含任意格式的文件(.c,.h等),但一般用于头文件的包含
宏定义
无参数的宏定义(宏常量)
#define num 100
在编译预处理的时候,将程序中在该语句以后出现的所有的num都用100代替。 在预编译是将宏名替换成字符串的过程称为“宏展开”。
宏定义,只在宏定义的文件中起作用。
说明:
- 宏名一般用大写,以便于变量区别
- 宏定义可以是常数、表达式等
- 宏定义不作语法检查,只有在编译被宏展开后的源程序才会报错
- 宏定义不是c语言,不在行末加分号
- 宏名有效范围为从定义到本源文件结束
- 可以用#undef命令终止宏定义的作用域
- 在宏定义中,可以引用已定义的宏名
带参数的宏定义(宏函数)
宏通过使用参数,可以创建外形和作用都与函数类似的类函数宏。宏的参数也用圆括号括起来
注意:
- 宏的名字中不能有空格,但是在替代的字符串中可以有空格,ANSIC允许在参数列表中使用空格
- 用括号括住每一个参数,并括住宏的整体定义
- 用大写字母表示宏的函数名
- 如果打算宏代替函数来加快程序运行速度。假如在程序中只使用一次宏对程序的运行时间没有太大提升
条件编译
基本概念
一般情况下,源程序中所有行都参加编译。但有时希望对部分源程序行只在满足一定条件时才编译,即对这部分源程序行指定编译条件
//测试存在
#ifdef 标识符
程序端1
#else
程序端2
#endif
//测试不存在:
#ifndef 标识符
程序段1
#else
程序段2
#endif
//根据表达式定义
#if 表达式
程序段1
#else
程序段2
#endif
常用于防止头文件被重复包含引用
#ifndef _SOMEFILE_H
#define _SOMEFILE_H
//需要定义的部分
#endif
一些特殊的预定宏
// __FILE__ 宏所在文件的源文件名
// __LINE__ 宏所在行的行号
// __DATA__ 代码编译的日期
// __TIME__ 代码编译的时间
动态库的封装和使用
库的基本概念
库可以简单看成一组目标文件的集合,将这些目标文件经过压缩打包之后形成的一个文件。像在windows这样的平台上,最常用的c语言库是由继承开发环境所附带的运行库,这些库一般由编译厂商提供
windows下静态库创建和使用
静态库的创建
- 创建一个新项目,在已安装的模板中选择“常规”,类型下选择“空项目”,在名称和解决方案名称中输入(例如:staticlib),点击确定
- 在结局方案资源管理器的头文件中添加mylib.h文件,在源文件中添加mylib.c文件
- 在mylib.h文件中添加如下代码
#ifndef TEST_H
#define TEST_H
int myadd(int a,int b)
#endif
- 在mylib.c文件中添加如下代码
#include"test.h"
int myadd(int a,int b)
{
return a+b;
}
- 静态库打开属性,在常规里面,将配置类型改为静态库
- 生成代码,这是在输出中,可以看到静态库已经被输出了
静态库的使用
- 将debug文件里面的.lib文件,已经静态库项目目录底下的.h文件复制到想要应用该静态库的项目下
- 在vs中右键项目,添加现有项,将上述两个文件全部添加,这时就可以在当前项目中调用了
- 在项目中调用库函数
静态库优缺点
- 静态库对函数库的链接是放在编译时期完成的,静态库的程序的链接阶段被复制到了程序中,和程序运行的时候没有关系
- 程序在运行时与函数库再无瓜葛,移植方便
- 浪费空间和资源,所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件
静态库 浪费资源、更新发布麻烦
动态库 节省资源、更新发布简单
动态库的声明使用
正常步骤与静态库的声明使用差不多,在选择属性中的配置类型时,选择动态库即可
将.lib .dll .h三个文件交给用户使用即可
//默认函数声明 外部是用不了的
//将这个函数称为内部函数
//int mySub(int a, int b);
//外部函数 需要特殊声明 __declspec(dllexport)
__declspec(dllexport) int mySub(int a, int b);
__declspec(dllexport)
动态链接库中定义有两种函数:导出函数(export function) 和内部函数(internal function)。导出函数可以被其他模块调用,内部函数在定义他们的DLL程序内部使用
在vs2019中,直接生成dll动态库,能在目录的上级目录中找到.dll文件,但是找不到.lib文件
这时需要新建项,选择模块定义文件(.def),这时选择生成即可生成.lib文件
面向接口编程
甲方实现
实现甲方接口,所有函数指针全都声明,并且将整个游戏框架搭建
//初始化游戏
//参数一 人物指针 参数二 人物姓名
typedef void(*INIT_GAME)(void** player, char* name);
//核心战斗
//如果战斗胜利 返回1 失败 返回0 参数一 玩家 参数二 副本难度
typedef int (*FIGHT_GAME)(void* player, int gameDiff);
//查看玩家信息
typedef void (*PRINT_GAME)(void* player);
//离开游戏
typedef void (*EXIT_GAME)(void* player);
//甲方功能实现
void playGame(INIT_GAME init ,FIGHT_GAME fight,PRINT_GAME printGame,EXIT_GAME exitGame)
{
//初始化游戏
void* player = NULL;
printf("请输入玩家的姓名:\n");
char buf[1024];
scanf("%s", buf);
init(&player, buf);
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("游戏结束,请再接再厉\n");
}
else
{
printf("恭喜胜利!\n");
//查看玩家当前状态
printf("玩家当前状态为:\n");
printGame(player);
}
//退出游戏
exitGame(player);
}
乙方实现
即将对应函数在乙方的.h和.c文件中汇总,并详细写出
动态数组
初始实现
将数组开辟到堆区,实现动态扩展
//动态数组结构体
struct dynamicArray
{
//真是在堆区开辟数组的指针
void** pAddr;
//数组容量
int m_Capacity;
//数组大小
int m_Size;
};
//初始化数组 参数 初始数组容量 返回值 数组指针
struct dynamicArray* init_dynamicArray(int* capacity)
{
if (capacity <= 0)
{
return NULL;
}
//给数组分配内存
struct dynamicArray* arr = malloc(sizeof(struct dynamicArray));
if (arr == NULL)
{
return NULL;
}
//给属性初始化
arr->pAddr = malloc(sizeof(void*)**capacity);
//容量初始化
arr->m_Capacity = capacity;
//大小初始化
arr->m_Size;
return arr;
}