C语言|指针进阶(2):常量指针和指针常量、数组参数和指针参数、函数指针

目录

🍎1.常量指针、指针常量

🍌1.1常量指针

🍌 1.2指针常量

 🍌1.3const即修饰指针,又修饰变量

🍌代码 

 🍎2.数组参数、指针参数

🍌2.1一维数组传参

🍌2.2二维数组传参

✍数组指针的内存原理

🍌2.3一级指针传参

✍思考

🍌2.4二级指针传参

✍思考:

🍎3.函数指针

🍌参考数组指针

🍌列1:

🍌列2:

🍌分析代码

✍代码1:

✍代码2:

✍改写函数

🍎4.函数指针数组

🍌应用:

✍案例1:

✍案例2:

🍎5.指向函数指针数组的指针

🍎6.回调函数

🍌qsort函数

✍使用qsort函数

✍实现qsort函数


🍎1.常量指针、指针常量

🍌1.1常量指针

语法:const  类型* 变量名;

  • const修饰指针
  • 指针指向可以修改,指针指向的指针不可以修改

🍌 1.2指针常量

语法: 类型*  const  变量名;

  • const修饰变量
  • 指针指向的值可以改变,指针指向不可以改变

 🍌1.3const即修饰指针,又修饰变量

语法: const  类型*  const  变量名;

  • 指针的指向和指针指向的值都不可以改

🍌代码 

int main()
{
	int a = 10;
	int b = 20;

	//1.const修饰的是指针,指针指向可以修改,指针指向的值不可以修改
	const int* p1 = &a;
	p1 = &b;
	//*p1=100;//报错,此表达式左值不可修改
	printf("p1=%d\n", *p1);

	//2.const修饰的是变量,为指针常量,指针的指向不可以改,指针指向的值可以修改
	int* const p2 = &a;
	*p2 = 100;
	//p2=&b;//报错,指针的指向不可以修改
	printf("p2=%d\n", *p2);
	printf("a=%d\n", a);

	//3.const即修饰指针又修饰常量,指针的指向和指针指向的值都不可以修改
	const int* const p3 = &a;
	printf("p3=%d\n", *p3);

	return 0;
}

 🍎2.数组参数、指针参数

🍌2.1一维数组传参

 void test(int arr[])//形参是数组
 {}
 ​
 void test(int* arr)//形参是指针
 {}
 ​
 void test(int arr[10])//形参是数组
 {}
 ​
 void text1(int** arr2)//形参是指针
 {}
 //此处arr2接收的是,数组arr2首元素的地址(以首元素为例)
 //第一次解引用,得到数组首元素的值(也是地址)
 //第二次解引用,得到首元素值(地址)对应的值
 ​
 void text1(int* arr[20])//形参是数组
 {}
 ​
 int main()
 {
     int arr[10] = { 0 };
     int* arr2[20] = { 0 };
 ​
     test(arr);
     text1(arr2);
     return 0;
 }

🍌2.2二维数组传参

 void test(int arr1[3][5])
 {}
 ​
 void test(int arr1[][])//错误用法
 {}
 ​
 void test(int arr1[][5])
 {}
 //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
 //这样才方便运算。
 ​
 void test(int* arr1)//用法错误,
 {}
 ​
 void test(int* arr1[5])
 {}
 //错误用法,指针数组
 //数组传过去需要用指针接收
 ​
 void test(int (*arr1)[5])
 {}
 //数组指针是指向数组的指针,接收数组的地址(&arr[0])
 //使用arr1接收二维数组的地址(第一行,一维数组的地址)
 //为解引用前,表示一行的地址(类型为int* [5]),解引用后表示类型为int [5]的数组的地址
 ​
 void test(int** arr1)//错误用法
 {}
 //二级指针是指向整数的指针的指针
 //arr1接收的是二维数组的地址
 //二维数组的地址与二维数组第一行的地址,二维数组第一个元素的地址相同,终究只是一个地址
 //这个地址所对应的空间存储的是首元素的值,解引用这个地址,得到准确数值而不是另一个地址
 //无法再次解引用,故不能使用二级指针
 ​
 int main()
 {
     int arr[3][5] = { 0 };
     test(arr);
     return 0;
 }

✍数组指针的内存原理

🍌2.3一级指针传参

 #include <stdio.h>
 void print(int *p, int sz)
 {
     int i = 0;
     for(i=0; i<sz; i++)
     {
         printf("%d\n", *(p+i));
     }
 }
 int main()
 {
     int arr[10] = {1,2,3,4,5,6,7,8,9};
     int *p = arr;
     int sz = sizeof(arr)/sizeof(arr[0]);
     //一级指针p,传给函数
     print(p, sz);
     return 0;
 }

✍思考

当一个函数的参数部分为一级指针的时候,函数能接收什么参数 ?

 //1
 int a=0;
 test(&a);
 ​
 //2
 int* ptr=&a;
 test(ptr);
 ​
 //3
 int arr[10];
 test(arr);

🍌2.4二级指针传参

 #include <stdio.h>
 void test(int** ptr)
 {
     printf("num = %d\n", **ptr);
 }
 int main()
 {
     int n = 10;
     int*p = &n;
     int **pp = &p;
     test(pp);
     test(&p);
     return 0;
 }

✍思考:

当函数的参数为二级指针的时候,可以接收什么参数?

 int a =0;
 int* p=&a;
 int** pp = &p;
 ​
 //1
 test(pp);
 ​
 //2
 test(&p);
 ​
 //3.指针数组
 int* arr[10];
 test(arr);

🍎3.函数指针

  • 指向函数的指针

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

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", &arr);

	printf("%p\n", Add);
	printf("%p\n", &Add);
    //Add和&Add都是函数的地址

	return 0;
}

🍌参考数组指针

int arr[10] = {0};
int (*p)[10] = &arr;

void add(int a,int b);
void (*p)(int,int) = add;
//void (*p)(int,int) = &add;

🍌列1:

 int Add(int x, int y)
 {
     return x + y;
 }
 ​
 int main()
 {
     int (*p)(int, int) = Add;
     int a = p(1, 2);
     //int a = (*p)(1,2);
     //也是可以的
     //&Add和Add是相同的地址
 ​
     printf("%d", a);
 ​
     return 0;
 }

🍌列2:

 void text(char (*p)[5])
 {
     for (int i = 0; i < 5; i++)
     {
         printf("%c ", *(*p + i));
     }
     printf("\n");
 }
 ​
 int main()
 {
     char arr[5] = { '1','2','3','4','7'};
     char(*p1)[5] = &arr;
 ​
     void (*p)(char(*)[5]) = text;
     p(p1);
     
     return 0;
 }

🍌分析代码

✍代码1:

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

✍代码2:

 void ( *signal( int,void(*)(int) ) )(int);//声明函数

表示函数signal( int,void(*)(int) )的指向int返回值为void

  1. 上述代码是一次函数声明

  2. 声明的函数为:signal

  3. signal函数的第一个参数是int类型

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

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

✍改写函数

 typedef void (*flat1)();
 typedef void (*flat2)(int);
 ​
 (*(flat1)0)();
 flat2 signal(int,flat2);

🍎4.函数指针数组

 //函数指针
 int (*p)(int,int);
 ​
 //函数指针数组
 int (*pfArr[])(int,int);
 ​
//对比
 int* p;//整形指针
 int* arr[];//整形指针数组

🍌应用:

✍案例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;
 }
 int main()
 {
     int x, y;
     int input = 1;
     int ret = 0;
     do
     {
         printf("*************************\n");
         printf(" 1:add 2:sub \n");
         printf(" 3:mul 4:div \n");
         printf("*************************\n");
         printf("请选择:");
         scanf("%d", &input);
         switch (input)
         {
         case 1:
             printf("输入操作数:");
             scanf("%d %d", &x, &y);
             ret = add(x, y);
             printf("ret = %d\n", ret);
             break;
         case 2:
             printf("输入操作数:");
             scanf("%d %d", &x, &y);
             ret = sub(x, y);
             printf("ret = %d\n", ret);
             break;
         case 3:
             printf("输入操作数:");
             scanf("%d %d", &x, &y);
             ret = mul(x, y);
             printf("ret = %d\n", ret);
             break;
         case 4:
             printf("输入操作数:");
             scanf("%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;
 }

✍案例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;
 }
 int main()
 {
     int x, y;
     int input = 1;
     int ret = 0;
     int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
     while (input)
     {
         printf("*************************\n");
         printf(" 1:add 2:sub \n");
         printf(" 3:mul 4:div \n");
         printf("*************************\n");
         printf("请选择:");
         scanf("%d", &input);
         if ((input <= 4 && input >= 1))
         {
             printf("输入操作数:");
             scanf("%d %d", &x, &y);
             ret = (*p[input])(x, y);
         }
         else
             printf("输入有误\n");
         printf("ret = %d\n", ret);
     }
     return 0;
 }

🍎5.指向函数指针数组的指针

 //函数指针
 int (*pf)(int,int);
 ​
 //函数指针数组
 int (*pfArr[4])(int,int);
 ​
 //指向函数指针数组的指针
 int (*(*ptr)[4])(int,int) = &pfArr;

🍎6.回调函数

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

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

🍌qsort函数

✍使用qsort函数

 typedef struct s
 {
     char name[20];
     int age;
 }s;
 ​
 int int_cmp(const void* p1, const void* p2)
 {
     return (*(int*)p2 - *(int*)p1);
 }
 int double_cmp(const void* p1, const void* p2)
 {
     return (*(double*)p2 - *(double*)p1);
 }
 int char_cmp(const void* p1, const void* p2)
 {
     return (*(char*)p2 - *(char*)p1);
 }
 int struct_age_cmp(const void* p1, const void* p2)
 {
     return (*(s*)p1).age - (*(s*)p2).age;
 }
 ​
 void int_print(void* arr, int num)
 {
     for (int i = 0; i < num; i++)
     {
         printf("%d ", *((int*)arr+i));
     }
     printf("\n");
 }
 void double_print(void* arr, int num)
 {
     for (int i = 0; i < num; i++)
     {
         printf("%.2f ", *((double*)arr + i));
     }
     printf("\n");
 }
 void char_print(void* arr, int num)
 {
     for (int i = 0; i < num; i++)
     {
         printf("%c ", *((char*)arr + i));
     }
     printf("\n");
 }
 void struct_print(void* arr, int num)
 {
     for (int i = 0; i < num; i++)
     {
         printf("%s:%d ", ((s*)arr+i)->name,(*((s*)arr+i)).age);
     }
 }
 ​
 int main()
 {
     int arr1[] = { 1,2,3,4,5,6,78,9,10 };
     double arr2[] = { 1.2,2.3,2.5,8.9,4.5,6.4,7.5 };
     char arr3[] = "abcdef";
     s arr4[] = { {"zhangsan",10},{"lisi",20},{"wangwu",18} };
 ​
     qsort(arr1, sizeof(arr1) / sizeof(arr1[0]), sizeof(arr1[0]), int_cmp);//int类型排序
     qsort(arr2, sizeof(arr2) / sizeof(arr2[0]), sizeof(arr2[0]), double_cmp);//double类型排序
     qsort(arr3, sizeof(arr3) / sizeof(arr3[0]), sizeof(arr3[0]), char_cmp);//char类型排序
     qsort(arr4, sizeof(arr4) / sizeof(arr4[0]), sizeof(arr4[0]), struct_age_cmp);//struct类型排序
 ​
     int_print(arr1, sizeof(arr1) / sizeof(arr1[0]));
     double_print(arr2, sizeof(arr2) / sizeof(arr2[0]));
     char_print(arr3, sizeof(arr3) / sizeof(arr3[0]));
     struct_print(arr4, sizeof(arr4) / sizeof(arr4[0]));
 ​
     return 0;
 }

✍实现qsort函数

 typedef struct s
 {
     char name[20];
     int age;
 }s;
 ​
 int cmp(void* p1,void* p2)
 {
     return (*(s*)p2).age - (*(s*)p1).age;
 }
 ​
 void swap(void* p1, void* p2,int size)
 {
     //因为传递的类型不明确,无法简单的通过它一个元素的空间的大小确定类型
     //强制类型转换为char*类型,并传递它一个元素空间的大小数值过来
     //通过循环一个字节,一个字节的传递数值,最终完全传递
     for (int i = 0; i < size; i++)
     {
         char temp = *((char*)p1 + i);
         *((char*)p1 + i) = *((char*)p2 + i);
         *((char*)p2 + i) = temp;
     }
 }
 ​
 void my_psort(void* arr4, int num, int size, int (*cmp)(void*,void*))
 {
     for (int i = 0; i < num; i++)
     {
         for (int j = 0; j < num-1-i; j++)
         {
             if (cmp((char*)arr4+j*size, (char*)arr4 + (j+1) * size) > 0)
             {
                 swap((char*)arr4 + j * size, (char*)arr4 + (j + 1) * size,size);
             }
         }
     }
 }
 ​
 void struct_print(void* arr, int num)
 {
     for (int i = 0; i < num; i++)
     {
         printf("%s:%d ", ((s*)arr+i)->name,(*((s*)arr+i)).age);
     }
     printf("\n");
 }
 ​
 int main()
 {
     s arr4[] = { {"zhangsan",10},{"lisi",20},{"wangwu",18} };
 ​
     my_psort(arr4, sizeof(arr4) / sizeof(arr4[0]), sizeof(arr4[0]), cmp);
 ​
     struct_print(arr4, sizeof(arr4) / sizeof(arr4[0]));
 ​
     return 0;
 }   

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

榶曲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值