Day22.C提高6
一、函数指针
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void func101()
{
printf("hello world\n");
}
//函数名其实就是函数的入口地址
void test101()
{
//printf("函数入口地址:%d", func101);
int* funcAddr = (int*)func101;
void(*myfunc)() = (void(*)())funcAddr;
myfunc();
}
int func102(int a, char b)
{
printf("a:%d b:%c\n", a, b);
return 0;
}
//如何去定义一个指向函数的指针
void test102()
{
//1.定义函数类型,通过类型来定义函数指针
typedef int(FUN_TYPE)(int, char);
FUN_TYPE* pFunc = func102;
pFunc(10, 'a');
(*pFunc)(20, 'b');
func102(30, 'c');
//2.直接定义函数指针类型
typedef int(*FUN_P)(int, char);
FUN_P pFunc2 = func102;
pFunc2(40, 'd');
//3.直接定义函数指针变量
int (*pFunc3)(int, char) = NULL;
pFunc3 = func102;
pFunc3(50, 'e');
//注:函数指针指向相同类型
//把指针转换为函数指针类型写法
int* pFunc4 = NULL;
(int(*)(int, char))pFunc4 = func102;
//void* pFunc4 = (int(*)(int, char))NULL;
//pFunc4 = func102;
pFunc(60, 'f');
}
//函数指针数组
void func1()
{
printf("func1\n");
}
void func2()
{
printf("func2\n");
}
void func3()
{
printf("func3\n");
}
void test103()
{
//函数指针数组定义方式
//先定义一个函数指针数组
void (*pFunc_Array[3])();
//给函数指针数组中的值进行赋值
pFunc_Array[0] = func1;
pFunc_Array[1] = func2;
pFunc_Array[2] = func3;
//调用
for (int i = 0; i < 3; i++)
{
pFunc_Array[i]();
}
}
//函数指针作函数参数(回调函数)
void printALLArray(void* arr, int eleSize, int len, void(*print)(void*))
{
char* start = (char*)arr;
//产生第i个元素的首地址,交给用户自定义的print函数来使用
for (int i = 0; i < len; ++i)
{
char* eleAddr = start + i * eleSize;
print(eleAddr);
}
}
void MyPrint(void* data)
{
int* p = (int*)data;
printf("%d\n", *p);
}
struct Person
{
char name[64];
int age;
};
//C语言不支持函数重载(C语言中函数名不可以重复)
void MyPrint_persons(void* data)
{
struct Person* p = (struct Person*)data;
printf("name:%s age:%d\n", p->name, p->age);
}
//函数指针作函数参数
void test104()
{
int arr[] = { 1,2,3,4,5 };
printALLArray(arr, sizeof(int), 5, MyPrint);
struct Person persons[] =
{
{"aaa",10},
{"bbb",20},
{"ccc",30},
{"ddd",40},
};
printALLArray(persons, sizeof(struct Person), 4, MyPrint_persons);
}
int main(void)
{
//test101();
//test102();
//test103();
test104();
system("pause");
return EXIT_SUCCESS;
}
二、链表的基本概念
链表基本概念:
链表是一种常用的数据结构,它通过指针将一些数据结点,连接成一个数据域。
链表由一系列结点组成,每个结点包含两个域,一个是数据域,数据域用来保存用户数据。另外一个是指针域,用来建立与下一个结点的联系(保存下一个结点的地址)。链表的内存是非连续的。
属性: 1.拿到链表的第一个结点,就相当于拿到了整个链表
2.头结点不保存任何数据
3.建立链表时无需预先知道数据总量,可以随机的分配空间,可以高效的在来弄表中的任意位置实时插入或删除数据
链表数组的优缺点比较:
数组优点:
可以用下标来查找数据,随机访问元素效率高。
数组缺点:
1.需要分配一块连续的存储区域(很大区域,有可能分配失败)
2.删除和插入某个元素效率低(在指定位置插入和删除会导致元素大量移动)。
链表优点(无需一次性分配一块连续的存储区域,只需分配n块结点存储区域,通过指针建立关系):
1.不需要一块连续的存储区域
2.删除和插入某个元素效率高(链表在指定位置插入和删除不需要移动元素,只需要修改指针即可)。
链表缺点:
访问的顺序性和组织链的空间损失:
随机访问元素效率低
相对于数组来讲,多了指针空间的开销。
三、链表分类
带头和不带头链表:
带头链表:固定一个结点作为头结点(数据与不保存有效数据),起一个标志位的作用,以后不管链表结点如何改变,此头结点固定不变。
不带头链表:头结点不固定,根据实际需要变换头结点(如在原来头结点前插入新的结点,然后,新结点作为链表的头结点)
静态链表、动态链表
单向链表、双向链表、循环链表、单项循环链表、双向循环链表
四、静态链表
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//链表结点类型定义
struct LinkNode
{
int data;
struct LinkNode* next;
};
void test201()
{
struct LinkNode node1 = { 10,NULL };
struct LinkNode node2 = { 20,NULL };
struct LinkNode node3 = { 30,NULL };
struct LinkNode node4 = { 40,NULL };
struct LinkNode node5 = { 50,NULL };
struct LinkNode node6 = { 60,NULL };
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node5;
node5.next = &node6;
//如何遍历整个链表?
//先定义一个辅助指针变量
struct LinkNode* pCurrent = &node1;
while (pCurrent != NULL)
{
printf("%d ", pCurrent->data);
//指针移动到下一个结点的首地址
pCurrent = pCurrent->next;
}
}
int main(void)
{
test201();
system("pause");
return EXIT_SUCCESS;
}
五、动态链表(分文件编写)
动态链表:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"LinkList.h"
void test301()
{
//初始化链表100 200 666 300 400 500 600
struct LinkNode* header = Init_LinkList();
//打印链表
Foreach_LinkList(header);
//插入数据
InsertByValue_LinkList(header, 300, 666);
//打印链表
printf("\n------------------\n");
Foreach_LinkList(header);
//删除666
RemoveByValue_LinkList(header, 666);
//打印链表
printf("\n------------------\n");
Foreach_LinkList(header);
//清空链表
Clear_LinkList(header);
//插入数据111 222 333 444
InsertByValue_LinkList(header, 1000, 111);
InsertByValue_LinkList(header, 1000, 222);
InsertByValue_LinkList(header, 1000, 333);
InsertByValue_LinkList(header, 1000, 444);
//打印链表
printf("\n------------------\n");
Foreach_LinkList(header);
//删除333
RemoveByValue_LinkList(header, 333);
//打印链表
printf("\n------------------\n");
Foreach_LinkList(header);
//删除443(链表中没有443)
RemoveByValue_LinkList(header, 443);
//打印链表
printf("\n------------------\n");
Foreach_LinkList(header);
//销毁链表
Destroy_LinkList(header);
}
int main(void)
{
test301();
system("pause");
return EXIT_SUCCESS;
}
LinkList.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#ifdef __cplusplus
extern "C" {
#endif
//定义结点数据类型
struct LinkNode
{
int data;
struct LinkNoda* next;
};
//初始化链表
struct LinkNode* Init_LinkList();
//在值为oldval的位置插入一个新的数据newval
void InsertByValue_LinkList(struct LinkNode* header, int oldval, int newval);
//删除值为delValue的结点
void RemoveByValue_LinkList(struct LinkNode* header, int delValue);
//遍历链表
void Foreach_LinkList(struct LinkNode* header);
//销毁链表
void Destroy_LinkList(struct LinkNode* header);
//清空链表
void Clear_LinkList(struct LinkNode* header);
#ifdef __cplusplus
}
#endif
LinkList.c
#include"LinkList.h"
//初始化链表
struct LinkNode* Init_LinkList()
{
//创建头结点
struct LinkNode* header = malloc(sizeof(struct LinkNode));
header->data = -1;
header->next = NULL;
//尾部指针
struct LinkNode* pRear = header;
//插入新的结点
int val = -1;
while (1)
{
printf("输入插入的数据:\n");
scanf("%d", &val);
if (val == -1)
{
break;
}
//先创建新节点
struct LinkNode* newnode = malloc(sizeof(struct LinkNode));
newnode->data = val;
newnode->next = NULL;
//新节点插入到链表中
pRear->next = newnode;
//更新尾部指针指向
pRear = newnode;
}
return header;
}
//在值为oldval的位置插入一个新的数据newval
void InsertByValue_LinkList(struct LinkNode* header, int oldval, int newval)
{
if (NULL == header)
{
return;
}
//两个辅助指针变量
struct LinkNode* pPrev = header;
struct LinkNode* pCurrent = pPrev->next;
while (pCurrent != NULL)
{
if (pCurrent->data == oldval)
{
break;
}
pPrev = pCurrent;
pCurrent = pPrev->next;
}
//如果pCurrent为NULL,说明链表中不存在值为oldval的结点
//写上此if语句,若不存在值为oldval的结点,则不做操作
//不写此语句,若不存在值为oldval的结点,将newval添加到链表最后
//if (pCurrent == NULL)
//{
// return;
//}
//先创建新节点
struct LinkNode* newnode = malloc(sizeof(struct LinkNode));
newnode->data = newval;
newnode->next = NULL;
//新结点插入到链表中
pPrev->next = newnode;
newnode->next = pCurrent;
}
//删除值为delValue的结点
void RemoveByValue_LinkList(struct LinkNode* header, int delValue)
{
if (NULL == header)
{
return;
}
//创建两个辅助指针变量
struct LinkNode* pPrev = header;
struct LinkNode* pCurrent = pPrev->next;
while (pCurrent != NULL)
{
if (pCurrent->data == delValue)
{
break;
}
//移动两个辅助指针
pPrev = pCurrent;
pCurrent = pPrev->next;
}
//如果pCurrent为NULL,说明链表中不存在值为delValue的结点
if (pCurrent == NULL)
{
return;
}
//重新建立待删除结点的前驱和后继结点关系
pPrev->next = pCurrent->next;
//释放删除结点的内存空间
free(pCurrent);
pCurrent = NULL;
}
//遍历链表
void Foreach_LinkList(struct LinkNode* header)
{
if (NULL == header)
{
return;
}
//辅助指针变量
struct LinkNode* pCurrent = header->next;
while (pCurrent != NULL)
{
printf("%d ", pCurrent->data);
pCurrent = pCurrent->next;
}
}
//销毁链表
void Destroy_LinkList(struct LinkNode* header)
{
if (NULL == header)
{
return;
}
//辅助指针变量
struct LinkNode* pCurrent = header;
while (pCurrent != NULL)
{
//先保存一下当前结点的下一个结点地址
struct LinkNode* pNext = pCurrent->next;
//释放当前结点内存
free(pCurrent);
pCurrent = NULL;
//pCurrent指向下一个结点
pCurrent = pNext;
}
}
//清空链表
void Clear_LinkList(struct LinkNode* header)
{
if (NULL == header)
{
return;
}
//辅助指针变量
struct LinkNode* pCurrent = header->next;
while (pCurrent != NULL)
{
//先保存一下当前结点的下一个结点地址
struct LinkNode* pNext = pCurrent->next;
//释放当前结点内存
free(pCurrent);
pCurrent = NULL;
//pCurrent指向下一个结点
pCurrent = pNext;
}
header->next = NULL;
}