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 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言进阶 之 指针详解(3) -> 正文阅读

[C++知识库]C语言进阶 之 指针详解(3)

函数指针

是指向函数的指针,存放函数地址。

#include<stdio.h>

int ADD(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("%p\n", &ADD);
	printf("%p\n", ADD);
	return 0;
}

00007FF6789113CF
00007FF6789113CF

两个打印的结果是一样的,都指向了同一个地址,可见 &函数名和函数名都是函数的地址。

整形指针 是 int* p, int 是数据类型,* 号表示p 是一个指针

数组指针 是 int (*p) [5] int [5] 是数组的类型,* 号同样表示p 是一个指针。

所以当指针指向函数的时候 ,我们只需要有函数的类型 ,和?* 号就可以了。

以int ADD(int x,int y) 为例子,指向这个函数的指针就可以这么写了

int (*pa)(int,int) =ADD;

第一个int 是函数的返回值,* 号表示pa 是一个指针,( )表示的指向函数的指针,这个括号里面的两个 int,int 是函数的参数类型。

#include<stdio.h>

int ADD(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 10;
	int b = 20;
	int (*pa)(int, int) = ADD;
	printf("%d\n",(*pa)(a,b));
	return 0;
}

?打印的结果:30

?使用函数的指针去调用函数。

printf("%d\n",(*pa)(a,b));?

函数调用的这句也可以写成:printf("%d\n",pa(a,b)); ADD 函数名本身也是一个地址,pa 指向的就是函数的地址,直接调用pa 就调用这个函数地址就直接调到函数。

void(*signal(int,void(*)int))(int); //1

typedef void(*pfun_i)(int); //2
pfun_i signal(int,pfuni);  

//1?

首先* 号和signal 并没有括号()括起来,()的优先级比* 高,所以先确定 signal 是过一个函数名,(int,void(*)int) 是这个函数的参数部分,把函数名和参数部分去掉剩下的就是,返回参数

void(*? ?)(int) 是这个函数的返回参数。

//2

上面函数的第二个参数类型和返回类型都是void(* )int,可以给这个类型自定义的名字让这个函数阅读起来比较简单一些。

在重定以时是这么定义的 typedef void(*pfun_i)(int) ,而不是?typedef void(*)(int)?pfun_i,因为这个类型在使用的时候不是void(*)(int)?p, 而是这么使用?void(*p)(int)。?

函数指针数组

存放函数指针的数组。

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

int MUL(int x, int y)
{
	return x * y;
}

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

int DIV(int x, int y)
{
	return x / y;
}

int main()
{
	int(*parr[4])(int, int) = { ADD,MUL,SUB,DIV };
	return 0;
}

?去掉函数名,剩下的返回值与函数参数就构成函数类型,这个四个函数的类型是一样的都是

int? ?(int,int)。类型一样的元素是可以使用数组来存储的。

? int(*parr[4])(int, int) = { ADD,MUL,SUB,DIV }; *号的优先级比[ ]优先级低所以这是个数组,把数组名去掉剩下的是数组类型

int(* [4])(int,int) 两部分 一是数组元素的个数[4] ,是4个元素,二是元素的类型 int(* )(int,int)指向函数的指针,函数指针

所以parr 就是一个函数指针数组啦。


#include<stdio.h>

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

int MUL(int x, int y)
{
	return x * y;
}

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

int DIV(int x, int y)
{
	return x / y;
}

int main()
{
	int(*parr[4])(int, int) = { ADD,MUL,SUB,DIV };
	int i = 0;
	for ( i = 0; i < 4; i++)
	{
		printf("%d\n", parr[i](6, 3));
	}
	return 0;
}

结果:

9
18
3
2

通过下标访问数组,传参,就是分别访问数组中的每个函数。?

指向函数指针数组的指针

指向函数指针数组的指针是一个指针?,该指针指向一个数组,数组的元素都是函数指针。

int main()
{
	int(*pfArr[4])(int, int);
	int(*(*ppfArr)[4])(int, int) = &pfArr;
	return 0;
}

?数组的类型是去掉数组名

int arr[10] 去掉数组名 arr 剩下的元素类型,数组元素个数int [10] 就是数组类型了

int a = 10;

int* p = &a;

使用类型、*来定义指针变量。

? ? int(*pf)(int,int)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//函数指针

? ? int(*pfArr[4])(int, int);? ? ? ? ? ? ? ? ? ? ? ? //函数指针数组
?? ?int(*(*ppfArr)[4])(int, int) = &pfArr;? ? //指向函数指针数组的指针

?去掉数组名pfArr,?? int(* [4])(int, int) 就是函数指针数组的类型,int(* (*) [4])(int, int)类型与*号就组成了 指向函数指针数组的指针。

回调函数

回调函数就是通过函数指针调用的函数。

#include<stdio.h>

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

int MUL(int x, int y)
{
	return x * y;
}

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

int DIV(int x, int y)
{
	return x / y;
}

void Calc(int(*pf)(int, int))
{
	int x = 6;
	int y = 2;
	printf("%d\n", pf(x, y));
}

int main()
{
	Calc(ADD);
	Calc(MUL);
	Calc(SUB);
	Calc(DIV);
	return 0;
}

结果:

?8
?12
?4
?3

Calc 以函数指针作为参数类型,调用时转递函数名,在内部使用函数指针调用到具体的函数。

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

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

int cmp_stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu *)p1)->age - ((struct Stu*)p2)->age;
}

int cmp_stu_by_name(const void* p1, const void* p2)
{

	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}

int main()
{
	struct Stu s[3] = { {"zhansan",20},{"lisi",10},{"wangwu",30}};
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	return 0;
}

使用到了库函数qsort, 函数的详细介绍与用法可以查看官方的文档

qsort - C++ Reference (cplusplus.com)

void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));

base 要排序的数组的第一个对象

num? 要排序的数组的元素个数

size? ?元素的大小,单位时字节

第四个参数是一个函数指针,这个函数实现比较功能。p1 < p2 ,返回值<0。p1 =p2,返回0。p1 >p2,返回值>0。

void* 表示无具体类型的指针,可以用来接收任意转递的过来的指针类型。指针类型表明了指针在解引用是的可影响的范围,void*因为是无具体类型的,所以也就不能解引用了。

函数参数在使用之前需要先转成实际的指针类型。

我们并没有直接调用比较函数的功能,而是将这个函数的地址传给了库函数,在库函数在做比较的时候,会回调回来到这个函数,再根据这个函数的结果,给我们转递的数组做好排序。

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

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

int cmp_stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu *)p1)->age - ((struct Stu*)p2)->age;
}

int cmp_stu_by_name(const void* p1, const void* p2)
{

	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}

void Swap(char* buff1, char* buff2, int width)
{
	int i = 0;
	for ( i = 0; i < width; i++)
	{
		char temp = *buff1;
		*buff1 = *buff2;
		*buff2 = temp;
		buff1++;
		buff2++;
	}
}

/**
*base: Pointer for the first element of target array
*size: numbers of the tartet array
*width: how much bytes for each element of this array
*cmp: perform to compare two element of this array
*/
void bubble_sort(void* base, int size, int width, int (*cmp)(void* e1, void* e2))
{
	int i = 0;
	for ( i = 0; i < size; i++)
	{
		int j = 0;
		for ( j = 0; j < size-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);
			}
		}
	}
}

int main()
{
	struct Stu s[3] = { {"zhansan",20},{"lisi",10},{"wangwu",30}};
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	return 0;
}

?

参考qsort 库函数实现了一个冒泡排序的方法。?

文章是基于比特鹏哥C语言学习视频的学习笔记,仅用于学习。请勿转载,请勿商用。?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 18:45:50  更:2022-08-19 18:49:44 
 
开发: 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年5日历 -2024/5/12 22:36:04-

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