C语言指针进阶

目录

字符指针

指针数组

数组指针

函数指针

函数指针数组

指向函数指针数组的指针

回调函数


字符指针

字符指针:char*

int main()
{
	char ch = 'w';
	char * pc = &ch;//pc是指向一个字符变量

	const char* p = "hello bit";//"hello bit"是一个常量字符串-存放在内存的常量区
	//上面表达式的作用是:把常量字符串"hello bit"的第一个字符h的地址赋值给p
	*p = 'w'; // "hello bit"放在常量字符串不能修改
	printf("%c\n", *p);
	printf("%s\n", p);

	

	return 0;
}

注意上面中的字符串是把字符串首元素的地址放在字符指针当中。

下面看一道面试题目:

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 same\n");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");

	return 0;
}

输出结果:

str1[]和str2[]是不同数组,数组名表示首元素的地址,他们的地址是不同的,所以第一个打印的是else里面的内容,str3和str4里面存放的是常量字符串首元素的地址,所以是相同的。

指针数组

我在前面指针初阶中讲过指针数组,我们简单复习即可。

指针数组,是数组,它是存放指针的数组。

int main()
{
	//char* arr[5];//arr是存放字符指针的数组
	//int* arr2[4];//arr2是存放整形指针的数组

	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	              //int* int* int* int*
	int* arr2[4] = {&a, &b, &c, &d};//arr2就是整型指针的数组
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d ", *(arr2[i]));
	}

数组指针

数组指针是指针,它是指向数组的指针。

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
    return 0; 
}

数组指针的使用:

void print1(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void print2(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}
}

void print3(int (*parr)[10], int sz)//这是一个错误的示范
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", parr[i]);//parr[i] == *(parr+i)
	}
}

void print4(int (*parr)[10], int sz)
{
	//*(parr + 0);//parr[0]
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		//printf("%d ", parr[0][i]);
		//printf("%d ", (*(parr + 0))[i]);
		printf("%d ", (*parr)[i]);//(*parr) 相当于 parr指向的数组的数组名
	}
}

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr) / sizeof(arr[0]);

	print4(&arr, sz);

	//print3(&arr, sz);
	//print2(arr, sz);
	//print1(arr, sz);//打印arr数组的内容

	return 0;
}

print1()和print2()一个是通过数组来接收参数,一个是通过指针来接收参数,这个在前面我们都已经知道了,但是在print3()里面我们用数组指针来接收参数,parr[i]存放的是地址,并没有找到里面的内容,而在print4里面先对parr进行*,相当于找到了数组的数组名,然后在通过[]访问数组里面的元素。

二维数组传参:

void print1(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
//数组指针
void print2( int(*p)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", *(*(p + i) + j));
			printf("%d ", p[i][j]);
			printf("%d ", (*(p + i))[j]);
		}
		printf("\n");
	}
}
//
int main()
{
	int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6,3,4,5,6,7 };
	//二维数组传参
	//print1(arr, 3, 5);
	print2(arr, 3, 5);//arr 是数组名,数组名是首元素地址
	return 0;
}

print1()就是通过二维数组来接收参数,print2()当中通过数组指针来接收参数。

函数指针

函数指针:指向函数的指针。

函数名和&函数名表示的含义是一样的,都是函数的地址。

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

int main()
{
	//printf("%p\n", &Add);
	//printf("%p\n", Add);//0x0012ff40 -  int(*)(int,int)
	//int (*pf)(int , int) = &Add;//pf是用来存放函数的地址 - pf就是函数指针变量

	int (*pf)(int, int) = Add;
	int ret = Add(2, 3);
	printf("%d\n", ret);//5

	//ret = (*pf)(4, 5);//9 *放在这相当于摆设
	ret = pf(4, 5); //9 跟上面一样
	printf("%d\n", ret);

	return 0;
}

在这里的pf和*pf的含义也是一样的,*在前面就是一个摆设,因为pf就相当于Add,所以不加*也可以。

下面我们看两个例子:

1.

int main()
{
	//代码是一次函数调用
	//解析:
	// 1. 代码中把0强制类型转换为类型为void (*)()的一个函数的地址。
	// 2. 解引用0地址,就是去0地址处的这个函数,被调用的函数是无参,返回类型是void
	// <C陷阱和缺陷>
	//
	(*( void (*)() ) 0)();

	return 0;
}

 2.

typedef void(* pfun_t)(int) ;
//typedef int int32;
int main()
{
	//这个代码是一次函数声明
	//声明的函数名signal
	//signal函数有2个参数,第一个是int类型,第二个是void(*)(int)的函数指针类型
	//signal函数的返回类型依然是:void(*)(int)的函数指针类型
	//
	void (* signal(int, void(*)(int) ) )(int);

	//简化
	pfun_t signal2(int, pfun_t); //利用typedef起别名

	//void (*)(int) signal(int, void(*)(int));//写法错误

	return 0;
}

函数指针数组

函数指针数组:就是数组中存放的是函数指针。

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

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

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

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

int main()
{
	//int* arr[10];//整型指针的数组
	//函数指针数组 - 存放函数指针的数组

	//int (*pf1)(int, int) = Add;
	//int (*pf2)(int, int) = Sub;
	//int (*pf3)(int, int) = Mul;
	//int (*pf4)(int, int) = Div;

	//pfArr就是一个函数指针的数组
	int (* pfArr[4])(int, int) = {Add, Sub, Mul, Div};

	return 0;
}

函数指针数组的使用:

写一个简单的计算器

void menu()
{
	printf("**********************************\n");
	printf("******* 1.add     2. sub    ******\n");
	printf("******* 3.mul     4. div    ******\n");
	printf("******* 0.exit              ******\n");
	printf("**********************************\n");
	//x&y x|y x>>y x<<y x^y
}
int Add(int x, int y)//int (*)(int, int)
{
	return x + y;
}

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

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

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

int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		menu();
		printf("请选择:>");//1
		scanf("%d", &input);

		//转移表 - 《C和指针》
		int (*pfArr[5])(int, int) = {0, Add, Sub, Mul, Div};
		                            //0   1   2    3    4
		if (input == 0)
		{
			printf("退出计算器\n");
		}
		else if(input>=1 && input<=4)
		{
			printf("请输入2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

方法二:

void menu()
{
	printf("**********************************\n");
	printf("******* 1.add     2. sub    ******\n");
	printf("******* 3.mul     4. div    ******\n");
	printf("******* 0.exit              ******\n");
	printf("**********************************\n");
	//x&y x|y x>>y x<<y x^y
}
int Add(int x, int y)//int (*)(int, int)
{
	return x + y;
}

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

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

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

void Calc(int(*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;

	printf("请输入2个操作数:>");
	scanf("%d %d", &x, &y);
	ret = pf(x, y); //回调函数--必须用函数指针来实现
	printf("ret = %d\n", ret);
}

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");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

方法二利用了回调函数。

指向函数指针数组的指针

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

回调函数

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

首先介绍下qsort()函数

void qsort(void* base, 
           size_t num, //待排序的元素个数
           size_t width, //一个元素的大小,单位是字节
           int(* cmp)(const void* e1, const void* e2) //cmp指向的是:排序时,用来比较2个元素的函数);

#include <stdlib.h>
#include <string.h>
int cmp_int(const void*e1,const void*e2)
{
	return *(int*)e1 - *(int*)e2;
}

void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

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(arr, sz);
}

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

int cmp_by_name(const void*e1,const void*e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

int cmp_by_age(const void*e1, const void*e2)
{
	return ((struct Stu*)e1)->name - ((struct Stu*)e2)->name;
}

void test2()
{
	struct Stu s[3] = { { "张三", 15 }, { "李四", 30 }, { "王五", 10 } };
	int sz = sizeof(s) / sizeof(s[0]);
	//按照名字排序
	//qsort(s, sz, sizeof(s[0]), cmp_by_name);

	//按照年龄排序
	qsort(s, sz, sizeof(s[0]), cmp_by_age);
}

void Swap(char* buf1, char* buf2,int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf1 = tmp;
		buf1++;
		buf2++;

	}
}


//使用回调函数实现一个通用的冒泡排序函数
void Bubblesort(void*base,int num,size_t width,int (*cmp)(const void*e1,const void*e2))
{
	int i = 0;
	//趟数
	for (i = 0; i < num - 1; i++)
	{
		//比较的对数
		int j = 0;
		for (j = 0; j < num - 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);
			}
		}
	}
}
//测试自定义的BubbleSort
void test3()
{
	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(arr, sz);
}

void test4()
{
	struct Stu s[3] = { { "张三", 15 }, { "李四", 30 }, { "王五", 10 } };
	int sz = sizeof(s) / sizeof(s[0]);
	//按照名字排序
	Bubblesort(s, sz, sizeof(s[0]), cmp_by_name);

	//按照年龄排序
	//Bubblesort(s, sz, sizeof(s[0]), cmp_by_age);
}

int main()
{
	//测试排序整形数组
	//test1();
	//测试排序结构体数组
	//test2();
	//测试自定义的Bubblesort
	//test3();
	//测试自定义的Bubblesort排序结构体
	test4();
	return 0;
}	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值