指针的相关实现

一、回调函数
1.1、回调函数定义

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

如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数

例如:使用函数指针实现计算器:

首先我们需要定义四个运算函数

  • Add(int x, int y):返回两个整数的和。
  • Sub(int x, int y):返回两个整数的差。
  • Mul(int x, int y):返回两个整数的乘积。
  • Div(int x, int y):返回两个整数的商。

然后需要构造辅助函数来让用户选择:

  • menu():打印出计算器的菜单选项。
  • calc(int (*pf)(int, int)):接受一个函数指针作为参数,然后提示用户输入两个操作数,并调用该函数指针指向的函数来计算结果,最后打印结果。

最后来写主函数:

  1. 定义一个整型变量 input,用于存储用户的选择。
  2. 使用 do-while 循环来反复显示菜单并接受用户输入。
  3. 调用 menu() 函数显示菜单。
  4. 提示用户输入选择,并读取到 input 变量中。
  5. 使用 switch 语句根据用户的选择调用不同的函数:
    • 如果用户选择 1,则调用 calc(Add) 进行加法运算。
    • 如果用户选择 2,则调用 calc(Sub) 进行减法运算。
    • 如果用户选择 3,则调用 calc(Mul) 进行乘法运算。
    • 如果用户选择 4,则调用 calc(Div) 进行除法运算。
    • 如果用户选择 0,则打印退出信息并退出循环。
    • 如果用户输入了其他值,则打印选择错误的信息。
  6. 当 input 为0时,do-while 循环结束。

代码如下:

//计算器
#include <stdio.h>
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;
}

void menu()
{
	printf("***************************\n");
	printf("****  1. add  2. sub   ****\n");
	printf("****  3. mul  4. div   ****\n");
	printf("****  0. exit          ****\n");
	printf("***************************\n");
}

void calc(int (*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int z = 0;
	printf("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	z = pf(x, y);
	printf("%d\n", z);
}

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函数的应用
2.1、使用qsort函数排序整型数据
#include <stdio.h>
//qsort函数的使用者得实现一个比较函数
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
//上述的代码相当于:
//int cmp_int(const void* p1, const void* p2)
//{
//	if (*(int*)p1 > *(int*)p2)
//		return 1;
//	else if (*(int*)p1 == *(int*)p2)
//		return 0;
//	else
//		return -1;
//}
int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int i = 0;
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

qsort函数的参数如下:

  • 第一个参数是要排序的数组。
  • 第二个参数是数组中的元素个数。
  • 第三个参数是每个元素的大小(以字节为单位)。
  • 第四个参数是比较函数。
2.2、使用qsort函数排序结构数据
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 struct Stu
{
	char name[20];
	int age;
};
 int cmp_stu_by_name(const void* p1, const void* p2)
	 {
	 	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
	 }
 int cmp_stu_by_age(const void* p1, const void* p2)
	 {
	 	return ((struct Stu*)p1)->age-((struct Stu*)p2)->age;
	 }
 void test1()
	 {
	 	struct Stu arr[3] = { {"zhangsan", 20},{"lisi", 35},{"wangwu", 18}};
	 	int sz = sizeof(arr) / sizeof(arr[0]);
	 	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
	 }
 void test2()
 {
	 struct Stu arr[3] = { {"zhangsan", 20},{"lisi", 35},{"wangwu", 18} };
	 int sz = sizeof(arr) / sizeof(arr[0]);
	 qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
 }
 int main()
 {
	 test1();
	 test2();
	 return 0;
 }
三、sizeof和strlen的对比
3.1、sizeof:

sizeof是操作符,计算操作数所占内存的大,单位是字节,不关注内存中存放什么数据

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof a);
	printf("%d\n", sizeof(int));
	return 0;
}

代码结果如下:

3.2、strlen 

strlen是库函数,需要包含头文件string.h,它是求字符串长度的,统计的是\0之前字符的个数,如果没有\0,就会持续往后找,可能会越界

#include <stdio.h>
int main()
{
	char arr1[3] = { 'a','b','c' };
	char arr2[] = "abc";
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));
	printf("%d\n", sizeof(arr1));
	printf("%d\n", sizeof(arr1));
	return 0;
}

这里需要注意的是:

  • strlen(arr1)的行为是未定义的,可能导致程序崩溃或输出不确定的值。
 四、数组和指针
4.1、一维数组
#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };//数组有几个元素?

	printf("%zd\n", sizeof(a));//16 -- sizeof(数组名)的场景
	printf("%zd\n", sizeof(a + 0));//a是首元素的地址-类型是int*, a+0 还是首元素的地址,是地址大小就是4/8
	printf("%zd\n", sizeof(*a));//a是首元素的地址,*a就是首元素,大小就是4个字节
	//*a == a[0] == *(a+0)
	printf("%zd\n", sizeof(a + 1));//a是首元素的地址,类型是int*,a+1跳过1个整型,a+1就是第二个元素的地址,4/8
	printf("%zd\n", sizeof(a[1]));//a[1]就是第二个元素,大小4个字节
	printf("%zd\n", sizeof(&a));//&a是数组的地址,数组的地址也是地址,是地址大小就是4/8个字节
	printf("%zd\n", sizeof(*&a));//1. *& 互相抵消了,sizeof(*&a) = sizeof(a) -16
	//2. &a 是数组的地址,类型是int(*)[4],对数组指针解引用访问的是数组, 计算的是数组的大小 -16
	//
	printf("%zd\n", sizeof(&a + 1));//&a+1是跳过整个数组后的那个位置的地址,是地址就是4/8个字节
	printf("%zd\n", sizeof(&a[0])); //首元素的地址,大小4/8个字节
	printf("%zd\n", sizeof(&a[0] + 1));//&a[0] + 1 -- 数组第二个元素的地址,大小是4/8个字节

	return 0;
}
4.2、字符数组

(1)、

#include <stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };

	printf("%d\n", sizeof(arr));//数组名单独放在sizeof内部了,计算的是数组的大小,单位是字节-6
	printf("%d\n", sizeof(arr + 0));//arr是数组名表示首元素的地址,arr+0还是首元素的地址,是地址就是4/8个字节
	printf("%d\n", sizeof(*arr));//arr是首元素的地址,*arr就是首元素,大小就是1个字节
	//*arr -- arr[0] - *(arr+0)
	printf("%d\n", sizeof(arr[1]));//arr[1] 是第二个元素,大小也是1个字节
	printf("%d\n", sizeof(&arr));//&arr 是数组地址,数组的地址也是地址,大小是4/8个字节
	//&arr -- char (*)[6]
	printf("%d\n", sizeof(&arr + 1));//&arr+1, 跳过整个数组,指向了数组后边的空间,4/8个字节
	printf("%d\n", sizeof(&arr[0] + 1));//第二个元素的地址,是地址就是4/8字节

	return 0;
}

(2)、

#include <string.h>
#include <stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//arr是首元素的地址,数组中没有\0,就会导致越界访问,结果就是随机的
	printf("%d\n", strlen(arr + 0));//arr+0是数组首元素的地址,数组中没有\0,就会导致越界访问,结果就是随机的
	printf("%d\n", strlen(*arr));//arr是首元素的地址,*arr是首元素,就是'a','a'的ascii码值是97
	//就相当于把97作为地址传递给了strlen,strlen得到的就是野指针, 代码是有问题的
	printf("%d\n", strlen(arr[1]));//arr[1]--'b'--98,传给strlen函数也是错误的
	printf("%d\n", strlen(&arr));//&arr是数组的地址,起始位置是数组的第一个元素的位置,随机值 x
	printf("%d\n", strlen(&arr + 1));//随机值 x-6
	printf("%d\n", strlen(&arr[0] + 1));//从第2个元素开始向后统计的,得到的也是随机值 x-1

	return 0;
}

(3)、

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//arr是数组名,单独放在sizeof内部,计算的是数组总大小,是7个字节
	printf("%d\n", sizeof(arr + 0));//arr表示数组首元素的地址,arr+0还是首元素的地址,是地址就是4/8
	printf("%d\n", sizeof(*arr));//arr表示数组首元素的地址,*arr就是首元素,大小是1字节
	printf("%d\n", sizeof(arr[1]));//arr[1]是第二个元素,大小1个字节
	printf("%d\n", sizeof(&arr));//&arr是数组的地址,是地址就是4/8
	printf("%d\n", sizeof(&arr + 1));//&arr是数组的地址,+1跳过整个数组,还是地址,是地址就是4/8个字节
	printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址,大小是4/8个字节

	return 0;
}

(4)、

#include <string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr + 0));//arr首元素的地址,arr+0还是首元素的地址,向后在\0之前有6个字符
	//printf("%d\n", strlen(*arr));//'a'-97, 出错
	//printf("%d\n", strlen(arr[1]));//'b'-98, 出错
	printf("%d\n", strlen(&arr));//&arr是数组的地址,也是从数组第一个元素开始向后找,6
	//&arr -- char (*)[7]
	//size_t strlen(const char* s);

	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//5

	return 0;
}
4.3、二维数组
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//a是数组名,单独放在sizeof内部,计算的是数组的大小,单位是字节 - 48 = 3*4*sizeof(int)
	printf("%d\n", sizeof(a[0][0]));//a[0][0] 是第一行第一个元素,大小4个字节 
	printf("%d\n", sizeof(a[0]));//a[0]第一行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小 16 个字节
	printf("%d\n", sizeof(a[0] + 1));//a[0]第一行的数组名,但是a[0]并没有单独放在sizeof内部,所以这里的数组名a[0]就是
	//数组首元素的地址,就是&a[0][0],+1后是a[0][1]的地址,大小是4/8个字节

	printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)表示第一行第二个元素,大小就是4
	printf("%d\n", sizeof(a + 1));//a作为数组名并没有单独放在sizeof内部,a表示数组首元素的地址,是二维数组首元素的地址,也就是
	//第一行的地址,a+1,跳过一行,指向了第二行,a+1是第二行的地址,a+1是数组指针,是地址大小就是4/8个字节

	printf("%d\n", sizeof(*(a + 1)));//1.a+1是第二行的地址,*(a+1)就是第二行,计算的是第二行的大小 - 16
	//2. *(a + 1) == a[1], a[1]是第二行的数组名,sizeof(*(a + 1))就相当于sizeof(a[1]),意思是把第二行的数组名单独放在
	//sizeof内部,计算的是第二行的大小
	printf("%d\n", sizeof(&a[0] + 1));//a[0]是第一行的数组名,&a[0]取出的就是数组的地址,就是第一行的地址
	//&a[0]+1 就是第二行的地址,是地址大小就是4/8个字节
	printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1)意思是对第二行的地址解引用,访问的就是第二行,大小是16字节
	printf("%d\n", sizeof(*a));//a作为数组名并没有单独放在sizeof内部,a表示数组首元素的地址,是二维数组首元素的地址,也就是
	//第一行的地址,*a就是第一行,计算的就是第一行的大小,16字节
	//*a == *(a+0) == a[0]
	printf("%d\n", sizeof(a[3]));//a[3]无需真实存在,仅仅通过类型的推断就能算出长度
	//a[3]是第四行的数组名,单独放在sizeof内部,计算的是第四行的大小,16个字节

	//sizeof(int);//4
	//sizeof(3 + 5);//4

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值