函数指针与多功能冒泡排序函数

目录

一.数组指针与函数指针

1.数组指针基本定义:

2.函数指针的基本定义:

3.代码阅读

二. void *类型的指针

三.库函数qsort

四.多功能冒泡排序函数的实现(bubble_sort)

1.冒泡排序思想的一般化(以排升序为例)

2.头文件

3.排序函数模块

4.主函数测试模块(测试排序结构体数组)


一.数组指针与函数指针

1.数组指针基本定义:

函数指针与多功能冒泡排序函数_第1张图片

int (*a)[10]=&arr;

该指针的内存访问权限为:10*sizeof(int)   -->40个字节

可以这样理解 :int决定了该指针访问权限的基本单位,[10]决定了访问权限的扩大倍数。

a+1可以跳过40个字节的内存

二维数组的数组名实质上就是数组指针(行指针),数组指针可以进行两次解引用:

比如:

int arr[10][10]={0};   arr就是数组指针;
  *(*(arr+1)+1);    可以访问到数组第二行第二列的元素。

上面例子中对数组指针的第一次解引用实质上是将其访问权限缩小到单个元素的字节数,*(arr+1)实质上相当于第二行首元素的地址(arr+1跳过了40个字节),是一个一级整形指针。

2.函数指针的基本定义:

 函数指针与多功能冒泡排序函数_第2张图片

注意:函数指针无须进行解引用就可以访问到函数

比如上图中我们若想要通过指针变量p调用Add函数可以这样写:p(形参a,形参b) ;

函数指针和数组指针是c语言中唯二在类型声明中需要对*和变量标识名进行结合性表示的两种指针类型 。

数组指针的类型书写示例: int (*)[10]   ; 表明指针类型为指向十个元素的行指针

函数指针的类型书写示例: int (*)(int,int) ;表示指针指向形参为(int,int),返回类型为int的函数

注意:变量声明时,被函数指针或数组指针类型修饰的标识名必须出现在表示与*结合的小括号中.

只有在强制类型转换时,可以这样写(int (*)[10])pa.

比如我们现在要将 int (*)(int ,int) 类型通过typedef定义为PtrFun ,书写方式如下:

typedef int(*PtrFun)(int,int) ;

(而不能书写成 typedef PtrFun int (*)(int,int)  ; )

3.代码阅读

函数指针与多功能冒泡排序函数_第3张图片

函数指针与多功能冒泡排序函数_第4张图片

该语句意义是将整形常量0强制转换为void(*)()类型的指针常量,然后对其进行解引用,由于0指向一个函数,该语句相当于调用了内存中地址为0处的函数(该函数返回类型和形参都为空)

函数指针与多功能冒泡排序函数_第5张图片

利用类型重定义可以简化该声明语句:

typedef void(*pfun)(int);

则声明语句可简化为:

  pfun signal (int,pfun);

二. void *类型的指针

1.void * 类型的指针不能进行任何形式的运算和解引用操作

2.void * 类型的指针变量可以用于接收任何类型的地址,因此常被用作接收地址的容器      (经常作为函数形参出现)

不同类型的指针变量本质差异是内存访问权限(字节数)的差异。 

三.库函数qsort

qsort可以对由任何数据类型构成的数组进行排序,包含在头文件

函数声明:

void qsort( void *base,

                    size_t num,

                    size_t width,

                    int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

形参说明:

void*base :用于接收待排序数组的首元素地址,由于元素类型不确定故而用void*来接收

size_t :代表无符号整形

size_t num:用于接收待排序数组中的元素个数

size_t width:用于接收待排序数组中单个元素所占的字节数

int (__cdecl *compare )(const void *elem1, const void *elem2 )

是函数指针,用于接收由用户自行设计的数据比较函数的地址

接下来我们用冒泡排序的算法模拟实现库函数qsort

四.多功能冒泡排序函数的实现(bubble_sort)

1.冒泡排序思想的一般化(以排升序为例)

函数指针与多功能冒泡排序函数_第6张图片

函数指针与多功能冒泡排序函数_第7张图片

根据冒泡排序的基本思想,无论我们要排序何种类型的数据,循环的基本架构都是相同的:


    int i,j;
    for (i = 0; i < sz - 1; i++)        
	{
		for (j = 0; j < sz - 1 - i; j++)
		{

		}
	}
    

sz代表数组中的元素个数

外层循环进行sz-1次,每次都会将一个最大元素移动到前sz-i个元素的最右端

所以运用冒泡排序对不同类型的数据进行排序,模块代码区别仅在于数据的比较与交换部分

所以数据的比较模块可以由排序函数的使用者通过函数指针的形式传入排序函数中

这也是库函数qsort的实现原理之一。

对于函数使用者设计的数据比较函数的要求:

比较函数首部:

   int  cmp (const void* a, const void * b);  

a ,b分别指向两个待比较的元素(要求形参要设计为空类型的指针)

比较函数的功能要求是:比较结果 大于 等于 小于 分别用函数返回值的正数 0 负数来表示

模拟库函数qsort我们将自行设计的排序函数bubble_sort的返回类型和形参设计如下:

typedef int (*Ptrcmp)(const void*, const void*);  

void bubble_sort (void* arr, size_t sz, size_t byte, Ptrcmp cmp);

void*arr:用于接收待排序数组的首元素地址,由于元素类型不确定故而用void*来接收

size_t:代表无符号整形

size_t sz:用于接收待排序数组中的元素个数

size_t byte:用于接收待排序数组中单个元素所占的字节数

Ptrcmp cmp:是函数指针,用于接收由用户自行设计的数据比较函数的地址

2.头文件

#pragma once
#include 
typedef int (*Ptrcmp)(const void*, const void*);   
/*将类型 int (*)(void*,void *)定义为Ptrcmp简化代码的书写*/
//让用户自行去设计比较函数(比较结果大于,等于,小于分别用正数,0,负数来表示)
void bubble_sort(void* arr, size_t sz, size_t byte, Ptrcmp cmp);

3.排序函数模块

函数指针与多功能冒泡排序函数_第8张图片

#include "sort.h"
//各种指针类型中访问权限为一个字节的指针类型为char* 
//所以多功能排序函数中的指针访问操作我们统一运用char*指针来进行(将void* 强转为char*)

//封装一个数据交换函数
//用于交换两个byte个字节大小的数据
//实现思路是逐个字节内容两两进行交换,从而实现整体的交换
void swap(size_t byte, void* a, void* b)
{
	int i = 0;
	char tem = 0;
	for (i = 0; i < byte; i++)
	{
		tem = *((char*)a + i);
		*((char*)a + i) = *((char*)b + i);
		*((char*)b + i) = tem;
	}
}



//将待排序数组的首元素地址arr,元素个数sz,每个元素所占的字节数byte
//以及用户自行设计的数据比较函数的地址传入排序函数
//size_t为无符号整形
void bubble_sort(void * arr,size_t sz,size_t byte,Ptrcmp cmp)
{
	int i = 0;
	int j = 0;
	int flag = 0;
	for (i = 0; i < sz - 1; i++)
	{
		flag = 1;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp(((char*)arr + j * byte), ((char*)arr + (j + 1) * byte)) > 0)  
			/*将待比较的两个元素的地址传入用户自行设计的比较函数中*/
			{
				swap(byte, (char*)arr + j * byte, (char*)arr + (j + 1) * byte);
                flag =0;
			}
		}
		if (flag)
		{
			break;
		}
	}
}

4.主函数测试模块(测试排序结构体数组)

#include "sort.h"

struct Node
{
	int num;
	char name[20];
};

若要排序结构体数组,那么用户就应该设计如下形式的结构体数据比较函数,并将其地址传入bubble_sort函数中:

int cmp1(const void* a, const void* b)
{
    return ((struct Node*)a)->num - ((struct Node*)b)->num;
}

若结构体数组共有十个元素,则bubble_sort 调用语句应为:

bubble_sort(arr, 10, sizeof(struct Node), cmp1);

函数指针与多功能冒泡排序函数_第9张图片

#include "sort.h"


struct Node
{
	int num;
	char name[20];
};




//自行设计一个数据比较函数,将其地址传入排序函数中
//测试案例中我们对结构体数组进行排序,因此比较函数要针对结构体类型数据进行设计
//比较结果大于 等于 小于 分别 用正数 0 负数来表示


int cmp1(const void* a, const void* b)  //根据num进行比较
{
	return ((struct Node*)a)->num - ((struct Node*)b)->num;
}
int cmp2(const void* a, const void* b)  //根据name[10]进行比较
{
	return strcmp(((struct Node*)a)->name, ((struct Node*)b)->name);
}

int main()
{
	struct Node arr[10]={{0,0}};
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &(arr + i)->num);
		scanf("%s", &(arr + i)->name);
	}
	bubble_sort(arr, 10, sizeof(struct Node), cmp1);
	for (i = 0; i < 10; i++)
	{
		printf("%d,%s\n", (arr + i)->num, (arr + i)->name);
	}
	bubble_sort(arr, 10, sizeof(struct Node), cmp2);
	for (i = 0; i < 10; i++)
	{
		printf("%d,%s\n", (arr + i)->num, (arr + i)->name);
	}
	return 0;
}

库函数qsort的实现思路和使用方式也是如此。

函数指针与多功能冒泡排序函数_第10张图片

你可能感兴趣的:(初学者日志,c语言)