C语言指针(进阶)

目录

字符指针

指针数组 

数组指针

 数组指针的使用

数组参数和指针参数 

一维数组传参

 二维数组传参

 函数指针

函数地址的存储 

 函数指针的应用​编辑

 代码分析

函数指针的用途

函数指针数组

指向函数指针数组的指针 

 回调函数

 用回调函数实现冒泡排序

void*指针

自己设计qsort函数实现冒泡排序


字符指针

C语言指针(进阶)_第1张图片

这里abcdef\0占7个字节,而char*类型只有4个字节,p里面存的是首元素地址,而不是所有元素,通过首元素地址就能找到后面字符的地址然后进行打印,遇到\0停止打印,千万不要理解为这里p存的是整个字符串

在有些编译器上上面的程序会正常运行,但是会报警告,这是因为"abcdef"是常量字符串,常量字符串意思是本身不能被修改

C语言指针(进阶)_第2张图片

 C语言指针(进阶)_第3张图片

 当我们对p里面的内容进行修改,然后进行调试,我们发现这里会出现错误,这是因为"abcdef"是常量字符串,不能被修改

C语言指针(进阶)_第4张图片

为防止我们写代码时出现错误,我们用const修饰就行

C语言指针(进阶)_第5张图片

当我们用char类型去存储字符串,然后打印也会报错,这是因为这里的char时字符类型,而后面是字符串,所以会出错,若想打印用char[]数组就行 

C语言指针(进阶)_第6张图片

C语言指针(进阶)_第7张图片

p1=p2原因:

                   p1里面放的是a的地址,p2里面放的也是a的地址,p1=p2说明p1和p2指向同一个地址,由于abcdef\0是常量字符串,它只能读不能写(也就是不能被改),由于它只能读不能写的特性,所以在电脑内存里只存了一份"abcdef",所以p1和p2指向了同一个地方

arr1!=arr2原因:

                  这里创建了俩个独立的数组arr1和arr2,里面分别放了"abcdef",也就是说内存里面有俩个"abcdef",一个存在arr1里面,另一个存在arr2里面,arr1和arr2都是首元素地址,因为是俩个不同的空间,所以arr1和arr2不相等

指针数组 

 指针数组:里面存放的全是其它数组首元素的地址

C语言指针(进阶)_第8张图片

C语言指针(进阶)_第9张图片

 p数组里面元素类型都是int *

C语言指针(进阶)_第10张图片

C语言指针(进阶)_第11张图片

p[i]=*(p+i) 

C语言指针(进阶)_第12张图片

C语言指针(进阶)_第13张图片

用解引用和二维数组的方式去打印,此时会报错

 

这是因为*p是int *类型,而此时里面的数字是int类型,所以不能用解引用的方式打印二维数组  *(p+i)=p[i]

       虽然打印起来像二维数组,但它仍然是一维数组,因为二维数组在内存里面是连续存放的,但这三个一维数组不一定连续存放

数组指针

 数组指针:存放整个数组地址的指针

int *p1[10]; //指针数组,p1和[]先结合
int (*p2)[10];//数组指针,p2和*先结合,p2指向[10],int说明数组类型是int

p2是数组指针,p2可以指向一个数组,该数组有10个元素,每个元素的类型是int类型

 C语言指针(进阶)_第14张图片

 数组名通常表示首元素地址,但是有俩个例外,

sizeof(数组名):计算的是整个数组的大小,注意括号是只能是数组名,不能出现其它任何的字符

&数组名:这里的数组名是整个数组,所以&数组名取出的是整个数组的地址

C语言指针(进阶)_第15张图片

&数组名+1表示跳过整个数组大小

a[0]+1表示跳过一个元素所占字节大小

总结:数组以各种方式在+1时候表示跳过其所占类型的大小

 C语言指针(进阶)_第16张图片

数组指针就是用来存放一个数组的地址的 ,这颗*就是告诉这里是指针而不是解引用,p2的类型是int  (*)[10],数组指针跟二级指针没任何关系,二级指针是用来存放一级指针的地址的,数组指针[]里面的大小不能忽略不写

C语言指针(进阶)_第17张图片

注意看,下面有警告

C语言指针(进阶)_第18张图片

这里p指向整个数组,*p就是数组名,也就是说*p就是首元素地址,如果要打印里面的值,对其解引用就行 ,数组指针一般不在一维数组使用,这里只不过是便于让人更加理解指针数组

 数组指针的使用

 C语言指针(进阶)_第19张图片

二维数组的数组名即首元素的地址表示第一行的地址,所以这里用数组指针进行接收 

*(p+i)表示拿到了第n行数组名,j表示某行第j个元素的地址,再解引用就行

*(p+i)可以写成p[i],*(*(p+i)+j)=*(p[i]+j)=p[i][j]

C语言指针(进阶)_第20张图片

 p的类型int(*)[5],*(p+i)一次性跳过类型所占大小,所以一次跳过一行,一次跳过5个int字节

总结:二维数组传参,传的首元素地址是第一行地址,第一行有一堆数组,所以数组指针进行接收,一维数组传参,传的是第一个元素地址,第一个元素只有一个,所以用int *p接收即可,即传过来一堆数组地址的时候用数组指针可以接收,传过来一个就用其它方式(但数组指针仍然可以接收)

C语言指针(进阶)_第21张图片

 C语言指针(进阶)_第22张图片

传一堆数组不能用int *p来接收,因为这里会出警告,实参 形参类型不同

C语言指针(进阶)_第23张图片 int (*parr3[10])[5];

parr3先和[]结合,再和*结合,把parr 3[10]拿出来剩int(*)[5],这是一个数组指针类型,parr3是一个存放数组指针的数组就是parr3[10]的类型是int(*)[5]

C语言指针(进阶)_第24张图片

数组指针数组:数组里的每一个元素是其余数组的所有地址,也就是一个数组里面全是数组指针

数组参数和指针参数 

一维数组传参

C语言指针(进阶)_第25张图片这里的二级指针也可以,因为arr2是指针数组,指针数组里面村的全是数组首元素地址,都是int *类型,而int *是一级指针,一级指针的地址存放在二级指针中 

 二维数组传参

C语言指针(进阶)_第26张图片C语言指针(进阶)_第27张图片最后一个是二级指针,而我们穿的二维数组的首元素也就是一行,也就是一维数组,一维数组不能放到二级指针中去,二级指针只是来存放一级指针地址的指针 

#include 
void print(int *p, int sz)
{
int i = 0;
for(i=0; i

一级指针传参,用一级指针接收

C语言指针(进阶)_第28张图片

二级指针传参,用二级指针接收, 

 函数指针

 指向函数的指针

 C语言指针(进阶)_第29张图片

 C语言指针(进阶)_第30张图片

函数名和&函数名打印出来的结果一模一样 ,二者拿到的都是函数的地址,没什么不一样的

函数地址的存储 

C语言指针(进阶)_第31张图片

设置一个指针指向函数,前面是函数的返回值类型,括号里是参数类型 ,()里面参数的名字写不写都无所谓,说清楚类型就可以

 函数指针的应用C语言指针(进阶)_第32张图片

 通过指针找到函数,就可以进行使用了

C语言指针(进阶)_第33张图片 *也可以不写,要写一定要放括号里面去,写多个*也可以使用 

C语言指针(进阶)_第34张图片

C语言指针(进阶)_第35张图片

也可以不写&因为&函数和函数名都是函数的地址,没什么区别

C语言指针(进阶)_第36张图片

 代码分析

//代码1

( *( void (*)() ) 0 )();

分析:void(*)()这是一个函数指针类型

         ( void(*)())0 这是对0进行强制类型转换,把0由int转换为函数指针类型,此时0不再是平常所理解的0,而0变为了一个地址,此时0地址中放了一个函数,只不过函数没有参数,返回类型为void

         (*( void(*)())0)()

这里相当于(*(函数指针))(),对函数指针解引用,也就是调用这个函数,先解引用找到这个函数,由于0地址的函是没有参数的,所以在调用的时候没有传参

C语言指针(进阶)_第37张图片

//代码2

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

C语言指针(进阶)_第38张图片 C语言指针(进阶)_第39张图片

 C语言指针(进阶)_第40张图片

 简化上面的代码

C语言指针(进阶)_第41张图片

函数指针的用途

这里写一个计算器,实现加减乘除 

#include
#include
void menu()
{
	printf("*****************************\n");
	printf("****   1. add   2. sub  *****\n");
	printf("****   3. mul   4. div  *****\n");
	printf("****   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 main()
{
	int input;
	int x = 0;
	int y = 0;
	int ret = 0;
	do 
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入俩个数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入俩个数:");
			scanf("%d %d", &x, &y);
			ret =Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入俩个数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入俩个数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出\n");
			break;
		default:printf("重新输入");
		}

	} while (input);
	return 0;
}

 这里很多地方的代码有些相似

C语言指针(进阶)_第42张图片 这里圈出来的代码都是重复的

 我们用函数指针来对该程序进行优化,

函数指针数组

C语言指针(进阶)_第43张图片

把函数指针放在数组中,一个数组中全是函数指针

 C语言指针(进阶)_第44张图片

 这里的函数必须几个函数的参数相同,返回类型也相同

 C语言指针(进阶)_第45张图片

 C语言指针(进阶)_第46张图片

#include
#include
void menu()
{
	printf("*****************************\n");
	printf("****   1. add   2. sub  *****\n");
	printf("****   3. mul   4. div  *****\n");
	printf("****   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 main()
{
	int input;
	do{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		int x = 0;
		int y = 0;
		int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
		if (input == 0)
			printf("退出\n");
		else if (input >= 1 && input <= 4)
		{
			printf("请输入俩个数字:");
			scanf("%d %d", &x, &y);
			int ret = pfArr[input](x,y);
			printf("%d\n", ret);
		}
		else
			printf("请重新输入");
	} while (input);
	return 0;
}

 C语言指针(进阶)_第47张图片

指向函数指针数组的指针 

C语言指针(进阶)_第48张图片 C语言指针(进阶)_第49张图片

 回调函数

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

 用回调函数实现冒泡排序

 不同类型在排序的时候,俩元素的比较方法不一样,我们可用qsort函数进行排序,qsort可以排序任意类型的数据

 C语言指针(进阶)_第50张图片

C语言指针(进阶)_第51张图片

 C语言指针(进阶)_第52张图片

void*指针

C语言指针(进阶)_第53张图片

C语言指针(进阶)_第54张图片

#include
#include
int cmp_int(const void* e1, const void* e2)//
{
	return (*(int*)e1 - *(int*)e2);//强制类型转换之后,在进行解引用
		
}
int main()
{
	int arr[] = {9,8,7,6,5,4,3,2,1,0};
	//0 1 2 3 4 5 6 7 8 9
	//把数组排成升序
	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]);
	}
	return 0;
}

 若要排降序,只需将cmp_int函数里面的e1和e2互换,qsort默认排升序

 C语言指针(进阶)_第55张图片

C语言指针(进阶)_第56张图片 C语言指针(进阶)_第57张图片

自己设计qsort函数实现冒泡排序

#include
#include
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++;
	}
}
int cmp_int(const void*e1,const void *e2)
{
	return (*(int*)e1 - *(int*)e2);
}
void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设数组是排好序
		//一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base + width * j, (char*)base + width * (j + 1))>0)
			{
				Swap((char*)base + width * j, (char*)base + width * (j + 1),width);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}
void test1()
{
	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 );
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	test1();
	return 0;
}

你可能感兴趣的:(C语言,c语言,开发语言)