指针专题--进阶指针2

目录

1.整型指针

(1)声明形式

(2)等价

2.字符指针

 3.数组指针

(1)声明形式

(2)辨:arr与&arr

(3)数组名

(4)数组指针的应用

4.指针数组

(1)声明形式

 (2)辨:int(*parr)[10]与int* parr[10]

5.函数指针

(1)声明形式

(2)样例分析

6.函数指针数组

(1)声明形式

(2)函数指针的应用

7.指向函数指针数组的指针

8.回调函数

(1)定义

(2)回调函数应用举例

(3)qsort函数及其使用

①参数形式

②运用样例(实现升序排列)

③运用样例(实现降序排列)

④模拟qsort函数实现冒泡排序的通用算法

9.总结


1.整型指针

整型指针--指向整型元素的指针

(1)声明形式

   int*  指针名  (如int* pa) 

例:利用指针访问数组元素

int main()
{	int arr[10] = { 10 };
	int* p = arr;
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;//更新数组元素为1,2,3,4,5,6,7,8,9
		printf("%d\n", *(p + i));
	}
	return 0;
}

(2)等价

arr[2]<==>p[2]<==>*(arr+2)<==>*(p+2)<==>*(2+p)<==>*(2+arr)<==>2[arr]

[ ]是一个操作符,2和arr是两个操作数,[]满足交换律arr[2]=2[arr]。

2.字符指针

指针可以指向字符串,其中存的实际为字符串首字母的首地址,但其必须为常量字符串。

例1:

int main() 
{
	//本质上是把“hello bit”这个字符串的首字符“h”的地址存储在了ps中
	const char * ps = "hello bit";//添加const表示常量字符串,不可更改
	char arr[] = "hello bit";//数组arr中存的是整个字符串“hello bit”,可以更改
	printf("%c\n", *ps);//输出"h"
	//输出整个字符串
	printf("%s\n", ps);//输出“hello bit”
	printf("%s\n", arr);//输出“hello bit”
	return 0;
}

注:const修饰,放在*右边,* const修饰指针变量本身不可更改;const放在*左边,const *修饰指针指向的内容不可更改。

例2:

int main()
{
	char str1[] = "hello bit";
	char str2[] = "hello bit";
	const char* str3 = "hello bit";//常量字符串,不能更改
	const char* str4 = "hello bit";//常量字符串,不能更改
	if (str1 == str2)
		printf("str1 and str2 are are same\n");
	else
		printf("str1 and str2 are not same\n");//输出str1 and str2 are not same
	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");//输出str3and str4 are same
	return 0;
}

图解:

 3.数组指针

整型指针-指向整型的指针(int *pa);字符指针-指向字符的指针(char *pc);数组指针-指向数组的指针,其中存放的是数组的地址。

(1)声明形式

指针指向元素的数据类型(*指针名)[数组大小]double* (*pd)[5]*pdpd与*结合(用括号括起来)说明他是一个指针;加[5]表示其指向有5个元素的数组;double*表示其指向数组的每个元素的数据类型为浮点型指针double*)。

例:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*parr)[10] = &arr;//*parr表示parr是指针;[10]表示parr指向有10个元素的数组;int表示其指向数组中每个元素的类型为整型int
	double* d[5];
	double* (*pd)[5] = &d;//*pd,pd与*结合(用括号括起来)说明他是一个指针;加[5]表示其	指向有5个元素的数组;double*表示其指向的数组中每个元素的数据类型为浮点型指针(double*)
	return 0;
}

(2)辨:arr&arr

&arr取出的是数组的地址

arr-数组名是数组首元素的地址,即arr[0]的地址

int main()
{
	int arr[10] = { 0 };
	int* p1 = arr;//arr-数组名是数组首元素的地址,即arr[0]的地址;是int类型的地址,应放到int类型的指针中
	int(* p2)[10] = &arr;//&arr取出的是数组的地址。P2指向整个数组,对pa进行解引用*p2相当于拿到了整个数组,即*p2等价于数组名arr
	//p1,p2结果一样,但类型不一样,如下例测试数据:
	printf("%p\n", p1);//输出0093F900
	printf("%p\n", p1+1);//输出0093F904
	//p1是整型指针,p1+1会跳过一个字节(即一个整型,4个字节)
	printf("%p\n", p2);//输出0093F900
	printf("%p\n", p2 + 1);//输出0093F928
	//p2是数组指针,p2+1会跳过一个数组(因为该数组有十个整型元素,即40个字节)
	return 0;
} 

(3)数组名

数组名是数组首元素的地址,但有两个例外。

①sizeof(数组名)-数组名表示整个数组,计算的是整个数组的大小,单位是字节。

②&数组名-数组名表示整个数组,取出的是整个数组的地址。

(4)数组指针的应用

二维数组的数组名表示首元素地址,二维数组的首元素是:第一行。

如:int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7,} };//该二维数组可理解为,有三个元素的数组,其中每个元素的类型为int[5],其数组名arr表示首元素地址,首元素是:第一行{1,2,3,4,5}。

例:打印二维数组

method1:用数组传参,用数组接收

void print1(int arr[3][5], int r, int c)
{//用int arr[3][5]接收数组
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

method2:用数组传参,用一维数组指针接收

void print2(int(*p)[5], int r, int c)
{//用int(*p)[5]指向一维数组的指针,接收二维数组的首地址,即一维数组的地址
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%d ", *(*(p + i) + j));
		}//p+i找到第i行的地址;解引用*(p+i)相当于得到第i行的数组名;
		//*(p+i)+j加j得到第i行下标为j的元素地址;解引用*(*(p+i)+j)得到第i行下标为j的元素
		printf("\n");
	}
}

4.指针数组

指针数组是一个存放指针的数组

(1)声明形式

变量类型* 数组名[大小]int* arr[3]arr[3]表示是一个有3个元素的数组,int* 表示数组的每个元素是一个整型指针

注:int arr[10];

//去掉数组名和元素个数剩下的就是数组元素类型 -- int

//去掉数组名,剩下的就是数组类型 -- int [10]

例:

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int b[] = { 2,3,4,5,6 };
	int c[] = { 3,4,5,6,7 };
	int* arr[3] = { a,b,c };//存三个数组首元素的地址,可以通过±整数获得数组中的其他元素
	for (int i = 0; i < 3; i++)
	{//打印三个数组a,b,c的内容
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(arr[i] + j));//arr[i]起始地址,arr[i]+1第二个元素地址,*(arr[i]+j)解引用,取出该地址存的元素。如arr[1],表示数组a[5]首元素1的地址;arr[1]+1表示数组a的第二个元素2的地址
		}
		printf("\n");
	}
	return 0;
}

注:可以理解为printf("%d ", *(arr[i] + j));等价于printf("%d ", arr[i][j]);

图解:

 (2)辨:int(*parr)[10]int* parr[10]

int(*parr)[10] = &arr;//数组指针   *parr表示parr是指针;[10]表示parr指向有10个元素的数组;int表示数组中每个元素的类型为整型int。

int* parr[10] = {a,b,c,d,e,f,g,h,i,j};//指针数组    parr[10]示是一个有10个元素的数组,int* 表示数组的每个元素是一个整型指针a,b,c,d,e,f,g,h,i,j为数组名,其中存放的为整数,如int a[5] = { 1,2,3,4,5 }···

5.函数指针

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

(1)声明形式

函数返回值类型 (*指针名)(函数参数类型),如:int (*pf)(int, int) = &Add

"*"与pf结合,表示pf是一个指针变量;(*pf)与括号(int,int)结合表示其是一个指向函数的指针,且该函数的参数为两个整型;最前方的int表示该函数的返回类型为int。即该指针变量pf指向一个有两个整型参数,返回类型也为整型的函数。

例:调用Add函数计算两数之和

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = &Add;
	//调用函数的三种方法
	//method1
	int ret = (*pf)(3, 5);//*pf解引用获得Add
	//method2因为对于函数&Add=Add没有区别,函数名就是函数的地址,而&Add=pf,即有Add=pf
	int ret = pf(3, 5);
	//method3
	int ret = Add(3, 5);
	printf("%d\n", ret);//输出8
	return 0;
}

注意:

数组名!=&数组名(数组名表示首元素的地址,&数组名表示整个数组的地址)

函数名==&函数名

(2)样例分析

(*(void(*)())0)();//调用0地址处的函数,该函数无参,返回类型是void

//1.void(*)() --表示是函数指针类型

//2.(void(*)())0 -- 对0进行强制类型转换,被解释为一个函数地址

//3.*(void(*)())0 -- 对0地址进行了解引用操作

//4.(*(void(*)())0)() -- 调用0地址处的函数

//signal是一个函数的声明

//method1

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

//==>等价于void(*)(int) signal(int,void(*)(int));但语法不允许这样写!!!

//1.signal和()先结合,说明signal是函数名

//2.signal函数的第一个参数的类型是int;第二个参数的类型是函数指针,该函数指针,指向一个参数为int,返回类型是void的函数

//3.signal函数的返回类型也是一个函数指针,该函数指针,指向一个参数为int,返回类型是void的函数

//method2,用typedef -- 对类型进行重定义

typedef void(*pfun_t)(int);//对void(*)(int)的函数指针类型重命名为pfun_t

//==>等价于typedef void(*)(int) pfun_t;但语法仍不允许这样写!!!需把名字pfun_t放括号里和*号一起

pfun_t signal(int, pfun_t);

6.函数指针数组

函数指针数组--存放函数指针的数组。

(1)声明形式

函数返回值类型 (*数组名[数组大小])(函数参数类型),如:int (*pfArr[2])(int, int) = { Add,Sub }Add,Sub 为两个函数的地址。pfArr优先与[ ]结合表示其为数组名;int arr[10]类似,去掉数组名和元素个数剩下的就是数组元素类型 -- int,去掉数组名与元素个数pfArr[2],剩下int(*)(int,int)即为数组元素类型--指向参数为两个整型,返回类型也为整型的函数的指针。

(2)函数指针的应用

原版://计算器 -- 实现整型变量的加、减、乘、除

#include<stdio.h>
int Add(int a, int b)
{
	return a + b;
}
int Sub(int a, int b)
{
	return a - b;
}
int Mul(int a, int b)
{
	return a * b;
}
int Div(int a, int b)
{
	return a / b;
}
void menu()
{
	printf("*************************************\n");
	printf("********** 1.add     2. sub *********\n");
	printf("********** 3.mul     4. div *********\n");
	printf("**********     0. exit      *********\n");
	printf("*************************************\n");
}
int main()
{
	int input = 0;
	do {
		menu();
		int x = 0, y = 0;
		int ret = 0;
		printf("请选择:>");
		scanf_s("%d", &input);
		switch(input)
		{
		case 1:
			printf("请输入2个操作数:>");
			scanf_s("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret=%d\n", ret);
			break;
		case 2:
		{
			printf("请输入2个操作数:>");
			scanf_s("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret=%d\n", ret);
			break;
		}
		case 3:
		{
			printf("请输入2个操作数:>");
			scanf_s("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret=%d\n", ret);
			break;
		}
		case 4:
		{
			printf("请输入2个操作数:>");
			scanf_s("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret=%d\n", ret);
			break;
		}
		case 0:
		{
			printf("退出程序\n");
			break;
		}
		default:
		{
			printf("选择错误,请重新选择!\n");
			break;
		}
		}
	} while (input);
	return 0;
}

升级1://计算器 -- 实现整型变量的加、减、乘、除

#include<stdio.h>
int Add(int a, int b)
{
	return a + b;
}
int Sub(int a, int b)
{
	return a - b;
}
int Mul(int a, int b)
{
	return a * b;
}
int Div(int a, int b)
{
	return a / b;
}
void menu()
{
	printf("*************************************\n");
	printf("********** 1.add     2. sub *********\n");
	printf("********** 3.mul     4. div *********\n");
	printf("**********     0. exit      *********\n");
	printf("*************************************\n");
}
int main()
{
	int input = 0;
	do {
		menu();
		//pfArr就是函数指针数组
		//转移表
		int(*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
		int x = 0, y = 0, ret = 0;
		printf("请选择:>");
		scanf_s("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:>");
			scanf_s("%d %d", &x, &y);
			ret = (pfArr[input])(x, y);//运用函数指针调用函数,通过不同下标访问不同元素,
			//不同元素中存放的是不同函数的地址,然后传参调用不同的函数
			//如:input=1,则定位指针数组下标为一的元素,即函数Add
			printf("ret=%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出程序\n");
			break;
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

③升级2://计算器 -- 实现整型变量的加、减、乘、除

见回调函数处

7.指向函数指针数组的指针

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

图解:

8.回调函数

(1)定义

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

(2)回调函数应用举例

升级2://计算器 -- 实现整型变量的加、减、乘、除

#inclede<stdio.h>
int Add(int a, int b)
{
	return a + b;
}
int Sub(int a, int b)
{
	return a - b;
}
int Mul(int a, int b)
{
	return a * b;
}
int Div(int a, int b)
{
	return a / b;
}
void menu()
{
	printf("*************************************\n");
	printf("********** 1.add     2. sub *********\n");
	printf("********** 3.mul     4. div *********\n");
	printf("**********     0. exit      *********\n");
	printf("*************************************\n");
}
int Calc(int (*pf)(int, int))//函数指针做参数
{
	int x = 0, y = 0;
	printf("请输入2个操作数:>");
	scanf_s("%d %d", &x, &y);
	return pf(x, y);//调用函数
}
int main()
{
	int input = 0;
	do {
		menu();
		int ret = 0;
		printf("请选择:>");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
		{
			ret = Calc(Add);
			printf("ret=%d\n", ret);
			break;
		}
		case 2:
		{
			ret = Calc(Sub);
			printf("ret=%d\n", ret);
			break;
		}
		case 3:
		{
			ret = Calc(Mul);
			printf("ret=%d\n", ret);
			break;
		}
		case 4:
		{
			ret = Calc(Div);
			printf("ret=%d\n", ret);
			break;
		}
		case 0:
		{
			printf("退出程序\n");
			break;
		}
		default:
		{
			printf("选择错误,请重新选择!\n");
			break;
		}
		}
	} while (input);
	return 0;
}

(3)qsort函数及其使用

①参数形式

void qsort(void* base,//base中存放的是待排序数据中第一个对象的地址

  size_t num,//排序数据元素的个数

  size_t size,//排序数据中一个元素的大小,单位是字节

  int (*cmp)(const void*,const void*)//是用来比较待排序数据中的2个元素的函数,第一个元素<第二个元素,返回<0的数;第一个元素=第二个元素,返回=0的数;第一个元素>第二个元素,返回>0的数。

//void* base无具体类型的指针,可以用于存放任意类型的指针变量(元素地址),但其不能直接解引用(*base)因为不知道数据类型,无法确定具体访问几个字节,base++也不知道向后跳过几个字节。因此使用时,需先强制转换成具体类型,才可运用。

②运用样例(实现升序排列)

#include<stdlib.h>
#include<string.h>
void print(int arr[], int sz)//打印数组
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int cmp_int(const void* e1, const void* e2)//参数表示要比较两个元素的地址
{
	return *(int*)e1 - *(int*)e2;//(int*)e1强制类型转化为整型指针后,*(int*)e1解引用访问数据;若e1指向的元素>e2指向的元素,则返回>0的数
}
void test1()
{
	int arr[] = { 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);//排序
	print(arr,sz);//打印
}
struct Stu
{	
	char name[20];
	int age;
};
①int sort_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//(struct Stu*)e1强制转化为结构体指针类型;((struct Stu*)e1)->age访问年龄,来进行排序
}
②int sort_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);//(struct Stu*)e1强制转化为结构体指针类型;((struct Stu*)e1)->name访问名字,来进行排序
}
void test2()
{
	struct Stu s[] = { {"zhangsan",30},{"lisi",34},{"wangwu",20} };//结构体初始化
	int sz = sizeof(s) / sizeof(s[0]);
	//①按照年龄来排序
	qsort(s, sz, sizeof(s[0]), sort_by_age);
	//②按照名字来排序
	qsort(s, sz, sizeof(s[0]), sort_by_name);}
int main()
{
	test1();//整型数据的排序
	test2();//使用qsort函数排序结构体数据
	return 0;
 }

③运用样例(实现降序排列)

只需改变比较函数

如:

int cmp_int(const void* e1, const void* e2)//参数表示要比较两个元素的地址
{
	return *(int*)e1 - *(int*)e2;//(int*)e1强制类型转化为整型指针后,*(int*)e1解引用访问数据,若e1指向的元素>e2指向的元素,则返回>0的数
}

只需将return *(int*)e1 - *(int*)e2;改为return *(int*)e2 - *(int*)e1;即可。

④模拟qsort函数实现冒泡排序的通用算法

#include<stdio.h>
void print(int arr[], int sz)//打印数组元素
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int cmp_int(const void* e1, const void* e2)//参数表示要比较两个元素的地址
{
	return *(int*)e1 - *(int*)e2;
}
void Swap(char* buf1, char* buf2, int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, int sz, int width,
	int (*cmp)(const void* e1, const void* e2))
{
	//趟数
	for (int i = 0; i < sz - 1; i++)
	{
		//一趟的排序
		for (int j = 0; j < sz - 1 - i; j++)
		{
			//两个元素比较
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            //(char*)base强制类型转换为字符指针,因为字符指针长度为1,可以实现+4(1*4),+8(1*8)等任意长度的跳跃
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
		}
	}
}
void test3()
{
	//整型数据的排序
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//排序
	bubble_sort(arr, sz, sizeof(arr[0]),cmp_int);
	//打印
	print(arr,sz);
}
int main()
{
	test3();
    return 0;
}

9.总结

int arr[5]:整型数组,存放的是5个整型元素。

int *parr1[10]:整型指针数组,存放的是10个指向整型元素的指针。

int (*parr2)[10]:数组指针,该指针能够指向一个数组,数组10个元素,每个元素的类型是int。

int (*parr3[10])[5]:是一种指针数组,parr3是一个存放数组指针的数组,该数组能够存放10个数组指针;每个数组指针能够指向一个数组,该数组有5个元素,每个元素是int类型。等价于挖掉parr3[10],剩下的就是这个数组里面的东西。

⑤int(*p1)(int,int):函数指针,指针p1指向一个参数为两个整型,返回类型也为整型的函数。

⑥int(* p2[4])(int,int):是一种指针数组,p2是一个存放函数指针的数组,该数组能够存放4个函数指针;每个函数指针指向一个参数为两个整型,返回类型也为整型的函数。

⑦int(* (*p3)[4])(int,int)=&p2:函数指针数组的指针,&p2取出的是函数指针数组的地址;p3就是一个指向【函数指针数组】的指针,该数组有4个元素,每个元素类型为int(*)(int,int)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值