百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术流 > 正文

C语言中链表的使用方法,函数指针的定义以及回调函数的使用

citgpt 2024-07-10 14:45 9 浏览 0 评论

1.链表的概念

链表也叫链式存储结构单链表,它用于存储逻辑关系为 "一对一" 的数据。

C语言中链表的使用方法,函数指针的定义以及回调函数的使用

使用链表存储的数据元素,其物理存储位置是随机的。

链表中每个数据元素在存储时都配备一个指针,用于指向自己的直接后继元素。

链表中的节点包含有:数据域和指针域

顾名思义,数据域就是存放数据的,指针域用于存放后继元素地址


链表分为:静态链表和动态链表

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

//1、静态链表

//节点的声明
struct LinkNode
{
    int num;
	struct LinkNode* next;//指针域
};

static void test01()
{
    //创建节点
    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 };

    //建立关系
    node1.next = &node2;
    node2.next = &node3;
    node3.next = &node4;
    node4.next = &node5;

    //如何遍历这个链表
    struct LinkNode* pCurrent = &node1;
    while (pCurrent != NULL)
    {
        printf("%d\n", pCurrent->num);
        pCurrent = pCurrent->next;
    }

}



//2、动态链表
static void test02()
{
    struct LinkNode* node1 = malloc(sizeof(struct LinkNode));
    struct LinkNode* node2 = malloc(sizeof(struct LinkNode));
    struct LinkNode* node3 = malloc(sizeof(struct LinkNode));
    struct LinkNode* node4 = malloc(sizeof(struct LinkNode));
    struct LinkNode* node5 = malloc(sizeof(struct LinkNode));

    node1->num = 10;
    node2->num = 20;
    node3->num = 30;
    node4->num = 40;
    node5->num = 50;

    //建立关系
    node1->next = &node2;
    node2->next = &node3;
    node3->next = &node4;
    node4->next = &node5;
    node5->next = NULL;

    //遍历链表
    struct LinkNode* pCurrent = node1;
    while (pCurrent != NULL)
    {
        printf(":::%d\n", pCurrent->num);
        pCurrent = pCurrent->next;
    }

    //释放节点
    free(node1);
    free(node2);
    free(node3);
    free(node4);
    free(node5);
}
int main01()
{
    //test01();
    test02();
    return 0;
}

2.链表的基础使用

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "02_linklist.h"


static void test01()
{
	//初始化链表
	struct LinkNode* pHeader = init_LinkList();

	//遍历链表
	printf("遍历链表的结果为\n");
	foreach_LinkList(pHeader);


	//插入数据
	// 100 10 200 20 30  300
	insert_LinkList(pHeader, 10, 100);
	insert_LinkList(pHeader, 20, 200);
	insert_LinkList(pHeader, -1, 300);

	printf("插入数据后,遍历链表的结果为\n");
	foreach_LinkList(pHeader);


	//测试 删除 
	delete_LinkList(pHeader, 30);
	delete_LinkList(pHeader, 100);
	delete_LinkList(pHeader, 1000);

	//  10 200 20 300
	printf("删除数据后,遍历链表的结果为\n");
	foreach_LinkList(pHeader);


	//清空链表
	clear_LinkList(pHeader);
	printf("清空数据后,遍历链表的结果为\n");
	foreach_LinkList(pHeader);


	insert_LinkList(pHeader, 111, 111);
	insert_LinkList(pHeader, 222, 222);
	insert_LinkList(pHeader, 333, 333);

	printf("清空后再次使用链表,遍历链表的结果为\n");
	foreach_LinkList(pHeader);

	//销毁链表
	destroy_LinkList(pHeader);
	pHeader = NULL;
}


int main02() 
{

	test01();

	system("pause");
	return EXIT_SUCCESS;
}
#include "02_linklist.h"

//初始化链表
struct LinkNode* init_LinkList()
{
	struct LinkNode* pHeader = malloc(sizeof(struct LinkNode));

	if (pHeader == NULL)
	{
		return NULL;
	}

	//pHeader->num = -1;  头节点不维护数据域
	pHeader->next = NULL; //头节点初始化指针域为NULL

	//创建一个尾节点,利用后期添加新的数据
	struct LinkNode* pTail = pHeader;

	int val = -1;

	while (1)
	{
		printf("请插入数据 -1代表输入结束:\n");

		scanf("%d", &val);

		if (val == -1)
		{
			break;
		}

		//创建新节点
		struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
		newNode->num = val;
		newNode->next = NULL;

		//建立关系
		pTail->next = newNode;
		//更新新的尾节点
		pTail = newNode;

	}
	return pHeader;
}

//遍历链表
void foreach_LinkList(struct LinkNode* pHeader)
{
	if (pHeader == NULL)
	{
		return;
	}
	//pCurrent 起始指向的是第一个有真实数据的节点
	struct LinkNode* pCurrent = pHeader->next;


	while (pCurrent != NULL)
	{
		printf("%d\n", pCurrent->num);
		pCurrent = pCurrent->next;
	}
}


//插入链表
void insert_LinkList(struct LinkNode* pHeader, int oldVal, int newVal)
{
	if (pHeader == NULL)
	{
		return;
	}

	//创建两个辅助指针变量
	struct LinkNode* pPrev = pHeader;
	struct LinkNode* pCurrent = pHeader->next;

	while (pCurrent != NULL)
	{
		if (pCurrent->num == oldVal) //找到插入位置
		{
			break;
		}

		//如果没有找到位置,让辅助指针后移
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}

	//创建新节点
	struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
	newNode->num = newVal;
	newNode->next = NULL;


	//建立关系  更新指针的指向
	newNode->next = pCurrent;
	pPrev->next = newNode;
}


//删除链表
void delete_LinkList(struct LinkNode* pHeader, int val)
{
	if (pHeader == NULL)
	{
		return;
	}

	//创建两个辅助指针变量
	struct LinkNode* pPrve = pHeader;
	struct LinkNode* pCurrent = pHeader->next;


	while (pCurrent != NULL)
	{
		if (pCurrent->num == val)
		{
			break;
		}
		pPrve = pCurrent;
		pCurrent = pCurrent->next;
	}

	//无效数据 就直接return
	if (pCurrent == NULL)
	{
		return;
	}

	//更改指针的指向
	pPrve->next = pCurrent->next;

	//删除节点
	free(pCurrent);
	pCurrent = NULL;
}


//清空链表
void clear_LinkList(struct LinkNode* pHeader)
{
	if (pHeader == NULL)
	{
		return;
	}

	//创建临时指针
	struct LinkNode* pCurrent = pHeader->next;

	while (pCurrent != NULL)
	{
		//先保存住待删除节点的后面的节点
		struct LinkNode* nextNode = pCurrent->next;

		free(pCurrent);
		pCurrent = nextNode;
	}

	//头节点的next置空
	pHeader->next = NULL;
}

//销毁链表
void destroy_LinkList(struct LinkNode* pHeader)
{
	if (pHeader == NULL)
	{
		return;
	}

	//先清空链表
	clear_LinkList(pHeader);
	//再释放头节点
	free(pHeader);
	pHeader = NULL;
}
#pragma  once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//节点声明
struct LinkNode
{
	int num;
	struct LinkNode* next;
};


//初始化链表
struct LinkNode* init_LinkList();

//遍历链表
void foreach_LinkList(struct LinkNode* pHeader);


//插入链表
void insert_LinkList(struct LinkNode* pHeader, int oldVal, int newVal);

//删除链表
void delete_LinkList(struct LinkNode* pHeader, int val);

//清空链表
void clear_LinkList(struct LinkNode* pHeader);

//销毁链表
void destroy_LinkList(struct LinkNode* pHeader);

3.函数指针的定义

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>


static void func()
{
    printf("hello world\n");
}

//函数指针的定义方式
static void test01()
{
    //先定义出函数类型,再通过类型定义出函数指针
    typedef void(FUNC_TYPE)();

    FUNC_TYPE* pFunc = func;

    pFunc();

}

static void test02()
{
    //先定义出函数指针类型,再定义函数指针
    typedef void(*FUNC_TYPE)();

    FUNC_TYPE pFunc = func;
    pFunc();
}

static void test03()//通常使用第三种
{
    //直接定义函数指针变量
    void(*pFunc)() = func;//void 是func的返回值  (*pFunc)是函数名称  ()是函数参数列表

    pFunc();
}



//函数指针的数组
static void func1()
{
    printf("func1的调用\n");
}
static void func2()
{
    printf("func2的调用\n");
}
static void func3()
{
    printf("func3的调用\n");
}

static void test04()
{
    //函数指针数组的定义
    void(*pFunc[3])();
    pFunc[0] = func1;
    pFunc[1] = func2;
    pFunc[2] = func3;

    for (int i = 0; i < 3; i++)
    {
        pFunc[i]();
    }
}


//函数指针  和  指针函数  的区别
//函数指针 是指向函数的指针
//指针函数 是函数的返回值是一个指针的函数


int main03()
{
    //test01();
    //test02();
    test03();
    return 0;
}

4.函数指针作为函数参数

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//提供一个函数,可以将任意的数据类型 打印出来
static void printText(void* a, void(*myPrint)(void*))
{
	myPrint(a);
}


static void myPrintInt(void* data)
{
	int* num = data;
	printf("%d\n", *num);
}
static void test01()
{
	int a = 100;
	printText(&a, myPrintInt);
}

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

static void myPrintPerson(void* data)
{
	struct Person* p = data;
	printf("姓名: %s 年龄: %d\n", p->name, p->age);
}

static void test02()
{
	struct Person p = { "aaa", 10 };
	printText(&p, myPrintPerson);
}

int main04() 
{

	//test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

5.回调函数

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//1、提供一个函数,可以打印任意类型的数组
static void printAllArray(void* arr, int eleSize, int len, void(*myFunc)(void*))
{
	char* p = arr;
	for (int i = 0; i < len; i++)
	{
		char* eleAddr = p + i * eleSize; //计算每个元素首地址
		//printf("%d\n", *(int*)eleAddr);

		myFunc(eleAddr);

	}
}



static void myPrintInt(void* data)
{
	int* num = data;
	printf("%d\n", *num);
}

static void test01()
{
	int arr[6] = { 1,2, 3, 4, 5, 6 };
	int len = sizeof(arr) / sizeof(int);
	printAllArray(arr, sizeof(int), len, myPrintInt);
}


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

static void myPrintPerson(void* data)
{
	struct Person* p = data;
	printf("姓名: %s 年龄: %d\n", p->name, p->age);
}
static void test02()
{
	struct Person pArray[] =
	{
		{ "aaa", 10 },
		{ "bbb", 20 },
		{ "ccc", 30 },
		{ "ddd", 40 },
	};
	int len = sizeof(pArray) / sizeof(struct Person);
	printAllArray(pArray, sizeof(struct Person), len, myPrintPerson);
}



// 参数1   数组首地址   参数2  每个元素占的内存空间   参数3  元素个数  参数4  查找的元素的地址 参数5  回调函数
int myFindPerson(void* arr, int eleSize, int len, void* data, int (*myCompare)(void*, void*))
{
	char* p = arr;
	for (int i = 0; i < len; i++)
	{
		char* eleAddr = p + i * eleSize; //获取每个元素的首地址

		//if ( eleAddr 和  data 的元素 相等  )
		if (myCompare(eleAddr, data))
		{
			return 1;
		}
	}
	return 0;
}


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

	if (strcmp(p1->name, p2->name) == 0 && p1->age == p2->age)
	{
		return 1;
	}
	return 0;

}

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

	struct Person p = { "ccc", 30 };
	int len = sizeof(pArray) / sizeof(struct Person);
	//查找数组中的元素,如果查找到返回1  ,找不到返回0 
	int ret = myFindPerson(pArray, sizeof(struct Person), len, &p, myComparePerson);

	if (ret)
	{
		printf("找到了元素\n");
	}
	else
	{
		printf("未找到元素\n");
	}
}


int main() {

	//test01();
	//test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

相关推荐

js中arguments详解

一、简介了解arguments这个对象之前先来认识一下javascript的一些功能:其实Javascript并没有重载函数的功能,但是Arguments对象能够模拟重载。Javascrip中每个函数...

firewall-cmd 常用命令

目录firewalldzone说明firewallzone内容说明firewall-cmd常用参数firewall-cmd常用命令常用命令 回到顶部firewalldzone...

epel-release 是什么

EPEL-release(ExtraPackagesforEnterpriseLinux)是一个软件仓库,它为企业级Linux发行版(如CentOS、RHEL等)提供额外的软件包。以下是关于E...

FullGC详解  什么是 JVM 的 GC
FullGC详解 什么是 JVM 的 GC

前言:背景:一、什么是JVM的GC?JVM(JavaVirtualMachine)。JVM是Java程序的虚拟机,是一种实现Java语言的解...

2024-10-26 08:50 citgpt

使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
  • 使用Spire.Doc组件利用模板导出Word文档
跨域(CrossOrigin)

1.介绍  1)跨域问题:跨域问题是在网络中,当一个网络的运行脚本(通常时JavaScript)试图访问另一个网络的资源时,如果这两个网络的端口、协议和域名不一致时就会出现跨域问题。    通俗讲...

微服务架构和分布式架构的区别

1、含义不同微服务架构:微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并...

深入理解与应用CSS clip-path 属性
深入理解与应用CSS clip-path 属性

clip-pathclip-path是什么clip-path 是一个CSS属性,允许开发者创建一个剪切区域,从而决定元素的哪些部分可见,哪些部分会被隐...

2024-10-25 11:51 citgpt

HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
  • HCNP Routing&Switching之OSPF LSA类型(二)
Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
  • Redis和Memcached的区别详解
Request.ServerVariables 大全

Request.ServerVariables("Url")返回服务器地址Request.ServerVariables("Path_Info")客户端提供的路...

python操作Kafka

目录一、python操作kafka1.python使用kafka生产者2.python使用kafka消费者3.使用docker中的kafka二、python操作kafka细...

Runtime.getRuntime().exec详解

Runtime.getRuntime().exec详解概述Runtime.getRuntime().exec用于调用外部可执行程序或系统命令,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。...

promise.all详解 promise.all是干什么的
promise.all详解 promise.all是干什么的

promise.all详解promise.all中所有的请求成功了,走.then(),在.then()中能得到一个数组,数组中是每个请求resolve抛出的结果...

2024-10-24 16:21 citgpt

Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解
  • Content-Length和Transfer-Encoding详解

取消回复欢迎 发表评论: