IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 指针详解(三) -> 正文阅读

[人工智能]指针详解(三)

前言

提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、回调函数

1、 回调函数定义

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传
递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一
方调用的,用于对该事件或条件进行响应。

示例1:简易计算器

上节课已经通过函数指针数组创建了一个简易计算器函数,这次可以通过回调函数来修改函数。

//计算器
#include <stdio.h>
void menu()
{
	printf("**************************\n");
	printf("**  1. add       2. sub **\n");
	printf("**  3. mul       4. div **\n");
	printf("**  5. xor       0. exit**\n");
	printf("**************************\n");
}

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}

int Xor(int x, int y)
{
	return x ^ y;
}
void Calc(int (*pf)(int, int))//Calc就是一个回调函数
{
	int x = 0;
	int y = 0;
	printf("请输入两个操作数:>");
	scanf("%d%d", &x, &y);
	printf("%d\n", pf(x, y));
}
int main()
{
	int input = 0;

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;
		case 0:
			printf("退出\n");
			;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}

2、qsort函数

qsort函数是一个C语言内置函数,在<string.h>中,可以用来排序各种类型的数据,其思想主要是快速排序思想。
在C语言官网中可以查到这个函数的各种参数:
C语言官网
或者在C语言标准库MSDN上也可以查找到。
在官网上是这样介绍的:

void qsort(void* base, 
	       size_t num, 
		   size_t width, 
		   int( *cmp)(const void *e1, const void *e2)
		   );
	//第一个参数:待排序数组的首元素地址
	//第二个参数:待排序数组的元素个数
	//第三个参数:待排序数组的每个元素的大小-单位是字节
	//第四个参数:是函数指针,比较两个元素的所用函数的地址-这个函数使用者自己实现
	//           函数指针的两个参数是:待比较的两个元素的地址

示例2:利用qsort函数实现数据升序排列

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu
{
	char name[20];
	int age;
};

//整形比较
int cmp_int(const void* e1, const void* e2)
{
	//比较两个整形值的
	return *(int*)e1 -*(int*)e2;
}
void test1()
{
	int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}


//浮点数比较
int cmp_float(const void*e1, const void*e2)
{
	/*if (*(float*)e1 == *(float*)e2)
		return 0;
	else if (*(float*)e1 > *(float*)e2)
		return 1;
	else
		return -1;*/

	return ((int)(*(float*)e1 - *(float*)e2));
}

void test2()
{
	float f[] = { 9.0, 8.0, 7.0, 6.0, 5.0, 4.0 };
	int sz = sizeof(f) / sizeof(f[0]);
	qsort(f, sz, sizeof(f[0]), cmp_float);
	int j = 0;
	for (j = 0; j < sz; j++)
	{
		printf("%f ", f[j]);
	}
}

//结构体比较
int cmp_stu_by_name(const void*e1, const void* e2)
{
	比较名字就是比较字符串
	字符串比较不能直接用><=来比较,应该用strcmp函数
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test3()
{
	struct Stu s[3] = { { "zhangsan", 20 }, { "lisi", 30 }, { "wangwu", 10 } };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
	test1();
	test2();
	test3();
	return 0;
}

相信到这里,你已经对qsort函数有了一定了解,接下来,在做一个案例加深印象。
示例3:利用回调函数,采用冒泡排序法模拟qsort
曾经,我们做过一个案例,使用冒泡排序法实现整形数据升序排序,但这个冒泡排序法有缺陷,只能排序整形数据,现在,我们可以利用回调函数,采用冒泡排序法模拟qsort,使冒泡排序法可以排序所有类型的数据。

//改进前的冒泡排序函数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void bubble_sort(int arr[], int sz)
{
	int i = 0;
	//趟数
	for (i = 0; i < sz-1; i++)
	{
		//一趟冒泡排序
		int j = 0;
		for (j = 0; j <sz-1-i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
//改进后的冒泡排序函数
void Swap(char* buf1, char*buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
//利用回调函数,模拟qsort创建的冒泡排序法
void bubble_sort(void*base, int sz, int width, int (*cmp)(void*e1, void*e2))
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		//每一趟比较的对数
		int j = 0;
		for (j = 0; j < sz-1-i; j++)
		{
			//两个元素的比较
			if (cmp((char*)base+j*width, (char*)base+(j+1)*width) > 0)
			{
				//交换
				Swap((char*)base + j*width, (char*)base + (j + 1)*width, width);
			}
		}
	}
}

struct Stu
{
	char name[20];
	int age;
};


//整形比较
int cmp_int(const void* e1, const void* e2)
{
	//比较两个整形值的
	return *(int*)e1 -*(int*)e2;
}
void test4()
{
	int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//使用bubble_sort的程序员一定知道自己排序的是什么数据
	//就应该知道如何比较待排序数组中的元素
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
}


//结构体比较
int cmp_stu_by_name(const void*e1, const void* e2)
{
	//比较名字就是比较字符串
	//字符串比较不能直接用><=来比较,应该用strcmp函数
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test5()
{
	struct Stu s[3] = { { "zhangsan", 20 }, { "lisi", 30 }, { "wangwu", 10 } };
	int sz = sizeof(s) / sizeof(s[0]);
	//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
	test4();
	test5();
	return 0;
}

在本例中,最难实现的有两个部分,一个使冒泡排序函数形参设计,另一个是排序判断实现。

void bubble_sort(void*base, int sz, int width, int (*cmp)(void*e1, void*e2))
//既然我们要排序各种数据,那么肯定有一种数组(数据类型)
//就应该有一个接收数组(数据类型)的形参,但是我们不知道我们设定的函数将来要排序说什么类型的数据
//所有应该设计成空类型,即void*base

//既然我们已经知道了要排序的数组,那么肯定要知道这个有多少个数组中有多少个元素,
//不然函数不知道什么时候结束排序,有可能会超出数组范围进行排序,
//所有应该有一个接收函数元素的形参即int sz

//已知数组和数组元素,进行排序时还要看具体的元素类型才能排序,
//例如,4个字节的整形数据与1个字节的字符比较没有具体意义,
//所以应该有有一个接收待排序数组的每个元素的大小,
//便于调用函数后访问数组具体查找几个字节,所以应该有字节长度接收即int width,。

//有了上面三个形参,我们要思考我们比较的是哪几个元素,
//是数组的第一个元素与数组的最后一个元素比较还是其他比较方式,
//所以我们要设计一个接收地址的指针,便于查找我们要排序数据的地址,
//因为不知要排序的数据类型,所以应该设计成空指针类型即int (*cmp)(void*e1, void*e2)。


if (cmp((char*)base+j*width, (char*)base+(j+1)*width) > 0)
			{
				//交换
				Swap((char*)base + j*width, (char*)base + (j + 1)*width, width);
			}
//比较的数据不同,字节长度也不同,访问的地址也不同
//所以将要访问的数组强制类型转换成一个字节长度的char*最为合适
//联系上面的字节长度,不同类型的字节长度不同,而相同类型的字节长度相同,
//所以就可以加上字节长度进行两个数据的比较

二、指针数组分析

代码如下:

int main()
{
	//数组名是首元素的地址
	//1. sizeof(数组名) - 数组名表示整个数组
	//2. &数组名 - 数组名表示整个数组
	
	//一维数组
	int a[] = { 1, 2, 3, 4 };//4*4 = 16
	printf("%d\n", sizeof(a));//sizeof(数组名)-计算的是数组总大小-单位是字节- 16
	printf("%d\n", sizeof(a + 0));//4/8 - 数组名这里表示收元素的值,a+0 还是首元素地址,地址的大小就是4/8个字节
	printf("%d\n", sizeof(*a));   //4 - 数组名表示首元素地址,*a就是首元素,sizeof(*a)就是4
	printf("%d\n", sizeof(a + 1));//4/8 - 数组名这里表示收元素的值,a+1 第2个元素的地址,地址的大小就是4/8个字节
	printf("%d\n", sizeof(a[1])); //4 - 第2个元素的大小
	printf("%d\n", sizeof(&a));   //4/8 &a取出的是数组的地址,但是数组的地址那也是地址,地址的大小就是4/8个字节
	printf("%d\n", sizeof(*&a));  //16 - &a数组的地址,数组的地址解引用访问的数组,sizeof计算的就是数组的大小单位是字节
	printf("%d\n", sizeof(&a + 1));//4/8   &a是数组的地址,&a+1虽然地址跳过整个数组,但还是地址,所以是4/8个字节
	printf("%d\n", sizeof(&a[0]));//4/8 &a[0]是第一个元素的地址
	printf("%d\n", sizeof(&a[0] + 1));//4/8  &a[0]+1 是第二个元素的地址

	//字符数组
	char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };

	printf("%d\n", strlen(arr));//随机值
	printf("%d\n", strlen(arr + 0));//随机值
	//printf("%d\n", strlen(*arr));//err
	//printf("%d\n", strlen(arr[1]));//err

	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值-6
	printf("%d\n", strlen(&arr[0] + 1));//随机值-1

	printf("%d\n", sizeof(arr));//sizeof计算机的是数组大小,6*1 = 6字节
	printf("%d\n", sizeof(arr + 0));//4/8 arr是首元素的地址,arr+0还是首元素的地址 地址的大小是4/8字节
	printf("%d\n", sizeof(*arr));   //1   arr是首元素的地址,*arr就是首元素,首元素是字符大小是一个字节
	printf("%d\n", sizeof(arr[1])); //1
	printf("%d\n", sizeof(&arr));   //4/8 &arr 虽然是数组的地址,但还是地址,地址大小是4/8个字节
	printf("%d\n", sizeof(&arr + 1));//4/8 &arr+1 是跳过整个数组后的地址,地址大小是4/8个字节
	printf("%d\n", sizeof(&arr[0] + 1));//4/8 第二个元素的地址
	return 0;
}
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:38:10  更:2021-07-29 11:39:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/19 11:46:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码