• 指针的进阶


    一.字符指针

    想到字符指针,我们就会突然想到,char,这里介绍一下char的使用方法

    int main() {
    	char ch = 'w';
    	char* pc = &ch;
    	*pc = 'w';
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    另外我们还可以这样使用。

    int main()
    {
        const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
        printf("%s\n", pstr);
        return 0; }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代码 const char* pstr = “hello bit.”;
    特别容易让同学们理解成,把内容放到了变量里面,其是/本质是把字符串 hello bit. 首字符的地址放到了pstr中。
    在这里插入图片描述

    相信经过上面的解释,大家对字符指针有了一些了解。
    我们再来看一个例子,来具体认识一下字符指针

    #include 
    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; }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    大家可以尽可能的猜一下结果,看看输出结果,是不是和你想的一样。

    在这里插入图片描述
    这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
    几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
    不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

    二. 指针数组

    顾名思义,指针数组的主语是数组,指针数组是一个存放指针的数组。
    看下面这一段代码。

    int* arr1[10]; //整形指针的数组
    char *arr2[4]; //一级字符指针的数组
    char **arr3[5];//二级字符指针的数组
    
    • 1
    • 2
    • 3

    例如

    char* a[5];
    
    • 1

    示意图
    在这里插入图片描述

    三. 数组指针

    数组指针是指针?还是数组?
    答案是:指针。
    我们已经熟悉:
    整形指针: int * pint; 能够指向整形数据的指针。
    浮点型指针: float * pf; 能够指向浮点型数据的指针。
    那数组指针应该是:能够指向数组的指针。

    3.1 数组指针如何定义的呢?

    int (*p2)[10];
    /*解释:
    p先和  *   结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
    这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.2 &数组名VS数组名

    #include 
    int main()
    {
        int arr[10] = {0};
        printf("%p\n", arr);
        printf("%p\n", &arr);
        return 0; }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    从这可见数组名和&数组名打印的地址是一样的。
    但是真的一样吗?
    我们又看下面这个代码

    #include 
    int main()
    {
     int arr[10] = { 0 };
     printf("arr = %p\n", arr);
     printf("&arr= %p\n", &arr);
     printf("arr+1 = %p\n", arr+1);
     printf("&arr+1= %p\n", &arr+1);
     return 0; }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    在这里插入图片描述

    3.3 数组指针的使用

    看下面的代码,我会对其逐一改造

    void print1(int arr[10], int sz)
    {
    	int i = 0;
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    	printf("\n");
    }
    void test1() {
    	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	print1(arr, sz);
    }
    int main() {
    	test1();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这上面,是我们平时操作数组打印的方法
    经过改造以后,就是如下的代码

    void print1(int *arr, int sz)
    {
    	int i = 0;
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ", *(arr+i));//通过指针来操作
    	}
    	printf("\n");
    }
    void test1() {
    	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	print1(arr, sz);
    }
    int main() {
    	test1();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    一维数组看了,我们再来看看二维数组

    void print3(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 test1() {
    
    	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    	print3(arr, 3, 5);
    }
    
    int main() {
    	test1();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    改造后的

    void print4(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("\n");
    	}
    }
    void test2() {
    	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    	print4(arr,3,5);
    }
    int main() {
    
    	test2();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    或许大家对我上面的*((*p+i)+j)的不理解,我画个示意图,大家看一下
    在这里插入图片描述

    四.数组传参

    这里有几种,我们还是举例大纲

    • 一维数组传参
    • 二维数组传参
    • 一级数组传参
    • 二级指针传参

    4.1一维数组传参

    #include 
    void test(int arr[])//ok?
    {}
    void test(int arr[10])//ok?
    {}
    void test(int* arr)//ok?
    {}
    void test2(int* arr[20])//ok?
    {}
    void test2(int** arr)//ok?
    {}
    // int** arr是不可行的
    
    int main()
    {
    	int arr[10] = { 0 };
    	int* arr2[20] = { 0 };
    	test(arr);
    	test2(arr2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.2 二维数组传参

    void test(int arr[3][5])//ok?//这种是可以的。
    {}
    void test(int arr[][])//ok? //这种是不合法的
    {}
    void test(int arr[][5])//ok?//这种是合法的
    {}
    //总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
    //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
    //这样才方便运算。
    void test(int* arr)//ok? 可行
    {}
    void test(int* arr[5])//ok?不可行的
    {}
    void test(int(*arr)[5])//ok?
    {}
    void test(int** arr)//ok? 不可行的
    {}
    int main()
    {
    	int arr[3][5] = { 0 };
    	test(arr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.3 一级指针传参

    #include 
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.3 二级指针传参

    #include 
    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; }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    五.函数指针

    函数指针的定义如下

    int(*变量名)(形参表达式,形参表达式)=&函数名();
    
    • 1

    从名字上来看,就是存放函数的指针。
    看下面的例子

    #include 
    void test()
    {
    	printf("hehe\n");
    }
    int main()
    {
    	printf("%p\n", test);
    	printf("%p\n", &test);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出结果
    在这里插入图片描述
    输出的是两个地址,这两个地址是 test 函数的地址。

    六.函数指针数组

    数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
    比如:

    int *arr[10];
    //数组的每个元素是int*
    
    • 1
    • 2

    那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
    答案是:parr1

    int (*parr1[10])();
    int *parr2[10]();
    int (*)() parr3[10];
    
    • 1
    • 2
    • 3

    parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
    是 int (*)() 类型的函数指针。

    我这里做一个例子
    一种是非函数指针实现计算器
    另一种是函数指针数组实现计算器

    普通方法

    int main()
    {
    	int input = 0;
    	int x = 0;
    	int y = 0;
    	int ret = 0;
    	do
    	{
    		menu();
    		printf("请选择:>");
    		scanf("%d", &input);
    		
    		switch (input)
    		{
    		case 1:
    			printf("请输入2个操作数:>");
    			scanf("%d %d", &x, &y);
    			ret = Add(x, y);
    			printf("%d\n", ret);
    			break;
    		case 2:	
    			printf("请输入2个操作数:>");
    			scanf("%d %d", &x, &y);
    			ret = Sub(x, y);
    			printf("%d\n", ret);
    			break;
    		case 3:
    			printf("请输入2个操作数:>");
    			scanf("%d %d", &x, &y);
    			ret = Mul(x, y);
    			printf("%d\n", ret);
    			break;
    		case 4:
    			printf("请输入2个操作数:>");
    			scanf("%d %d", &x, &y);
    			ret = Div(x, y);
    			printf("%d\n", ret);
    			break;
    		case 0:
    			printf("退出计算器\n");
    			break;
    		default:
    			printf("选择错误\n");
    			break;
    		}
    	} while (input);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    函数指针数组

    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");
    
    }
    int main() {
    	int input = 0;
    	int x = 0;
    	int y = 0;
    	int ret = 0;
    	//确立函数指针数组 
    	int (*pfArr[])(int, int) = { 0,Add,Sub,Mul,Div };
    	
    	do {
    		menu();
    		scanf("%d",&input);
    		if (input ==0) 
    		{
    			printf("退出计算器");
    			break;
    		}
    		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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

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

    void test(const char* str) {
     printf("%s\n", str);
    }
    int main()
    {
     //函数指针pfun
     void (*pfun)(const char*) = test;
     //函数指针的数组pfunArr
     void (*pfunArr[5])(const char* str);
     pfunArr[0] = test;
     //指向函数指针数组pfunArr的指针ppfunArr
     void (*(*ppfunArr)[5])(const char*) = &pfunArr;
     return 0; 
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    端到端自动驾驶:终局还是误区?
    【OpenCV-Python-课程学习(贾)】OpenCV3.3课程学习笔记:图像色彩空间的转换(cvtColor、)
    多线程开发中,多用消息传递,少用锁
    TypeScript_基本类型
    谈谈Vue项目打包的方式
    IO流文件相关部分
    小码农也有大目标,最新BAT大厂Java面试总结
    200. 岛屿数量(Java、DFS、岛屿题目)
    统一消息分发中心设计
    Python基础知识|50道必备的Python面试题(建议收藏)
  • 原文地址:https://blog.csdn.net/qq_45726327/article/details/126497037