• C/C++总结笔记——指针2:二级指针、指针数组&数组指针、函数指针&函数指针数组、二维指针数组&数组指针


    先看一道经典的C语言题:用变量a给出下面的定义

    a) 一个整型数(An integer)
    b)一个指向整型数的指针( A pointer to an integer)
    c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
    d)一个有10个整型数的数组( An array of 10 integers)
    e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
    f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)
    g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
    h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 ( An array of ten pointers to functions that take an integer argument and return an integer )

    // a) 一个整型数
    int a; 
    // b)一个指向整型数的指针
    int *a;
    // c)一个指向指针的的指针(二级指针),它指向的指针是指向一个整型数
    int **a; 
    // d)一个有10个整型数的数组
    int a[10]; 
    // e) 一个有10个指针的数组,该指针是指向一个整型数的
    int *a[10]; 
    // f) 一个指向有10个整型数数组的指针
    int (*a)[10]; 
    // g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
    int (*a)(int); 
    // h)  一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
    int (*a[10])(int);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.二级指针(c)

    如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针
    当函数参数为指针,我们需要动态的改变指针指向的地址(即传入不同指针)时(不使用引用),当传入的参数是二级指针变量的地址,对一级指针进行修改,则相当于对原指针地址进行了修改,

    1、二级指针作为参数传递,可以实现改变参数指针指向的位置。

    int* b = new int(2);
    void Func(int** num){
        *num = b;
    }
    int main(){
        int* a = new int(1);
        Func(&a);
        cout<<*a<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、二级指针可用于内存分配

    void  my_malloc(char **s)  {  
    	*s=(char*)malloc(100);  
    }  
    void  main(){  
    	char *p=NULL;
    	my_malloc(&p);
    	if(p)
    		free(p);  
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.指针数组与数组指针(f)&(g)

    #include 
    int main(){
        int p[3]={10,20,30};
        int *p1[3];
        int (*p2)[3];
    
        p1[0]=&p[0];
        p1[1]=&p[1];
        p1[2]=&p[2];
        printf("*p1[0] = %d, *p1[1] = %d, *p1[2] = %d\n", *p1[0], *p1[1], *p1[2]);
        printf("p1[0] = %#X, p1[1] = %#X, p1[2] = %#X\n", p1[0], p1[1], p1[2]);
        printf("&p1[0] = %#X, &p1[1] = %#X, &p1[2] = %#X\n", &p1[0], &p1[1], &p1[2]);
    
        p2=&p;//p2=p;会出现 warning,但不影响结果
        printf("(*p2)[0] = %d, (*p2)[1] = %d, (*p2)[2] = %d\n", (*p2)[0], (*p2)[1], (*p2)[2]);
        printf("&((*p2)[0]) = %#X, &((*p2)[1]) = %#X, &((*p2)[2]) = %#X\n", 
                &((*p2)[0]), &((*p2)[0]), &((*p2)[0]));
    
        printf("p = %#X, p1 = %#X, p2 = %#X\n", p, p1, p2);
        printf("*p = %#X, *p1 = %#X, *p2 = %#X\n", *p, *p1, *p2);
        printf("**p1 = %d, **p2 = %d\n", **p1, **p2);
        
        printf("sizeof(p) = %d, sizeof(p1) = %d, sizeof(p2) = %d\n", sizeof(p), 
                sizeof(p1), sizeof(p2));
        return 0;
    }
    /*输出
    *p1[0] = 10, *p1[1] = 20, *p1[2] = 30
    p1[0] = 0X61FE0C, p1[1] = 0X61FE10, p1[2] = 0X61FE14
    &p1[0] = 0X61FDF0, &p1[1] = 0X61FDF8, &p1[2] = 0X61FE00
    
    (*p2)[0] = 10, (*p2)[1] = 20, (*p2)[2] = 30
    &((*p2)[0]) = 0X61FE0C, &((*p2)[1]) = 0X61FE0C, &((*p2)[2]) = 0X61FE0C
    
    p = 0X61FE0C, p1 = 0X61FDF0, p2 = 0X61FE0C
    *p = 0XA, *p1 = 0X61FE0C, *p2 = 0X61FE0C
    **p1 = 10, **p2 = 10
    
    sizeof(p) = 12, sizeof(p1) = 24, sizeof(p2) = 8
    */
    
    • 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

    指针数组
    对于语句“intp1[3]”,因为“[]”的优先级要比“”要高,所以 p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,而“int*”修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 3 个指向 int 类型数据的指针,因此,它是一个指针数组。

    数组指针
    对于语句“int(p2)[3]”,“()”的优先级比“[]”高,“”号和 p2 构成一个指针的定义,指针变量名为 p2,而 int 修饰的是数组的内容,即数组的每个元素。也就是说,p2 是一个指针,它指向一个包含3个 int 类型数据的数组。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。

    数组指针与数组名的关系
    p2=&p;//p2=p;会出现 warning,但不影响结果。(有些编译器直接报错)
    在这里&p 是指整个数组的首地址,而 arr仅仅是数组首元素的首地址。赋值符号“=”号两边的数据类型不同(正常必须相同),则需要显示或隐式类型转换,因此发生警告可以运行。

    3.函数指针&函数指针数组

    函数指针: 一个指向函数的指针。一般用函数名表示。区别于返回值为指针的函数。
    函数指针数组:元素为函数指针的数组。转移表。c语言中函数不可以定义为数组,只能通过定义函数指针来操作。

    1、函数指针作为参数传递给另一个函数(实现回调函数)
    回调函数:将一个函数指针作为参数传递给其它函数。后者将“回调”用户函数。
    回调函数作用是为降低模块间的耦合度,针对不同的回调函数,可以用一个统一的函数接口调佣,是
    实现软件的高内聚低耦合的一种方法。

    函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。

    #include
    
    int Callback_1(){// Callback Function 1
        printf("Hello, this is Callback_1 \n");
        return 0;
    }
    int Callback_2(){// Callback Function 2
        printf("Hello, this is Callback_2 \n");
        return 0;
    }
    int Callback_3(){// Callback Function 3
        printf("Hello, this is Callback_3 \n");
        return 0;
    }
    
    int Handle(int (*Callback)()){
        printf("Entering Handle Function. \n");
        Callback();
        printf("Leaving Handle Function. \n");
    }
    
    int main(){
        printf("--------------------------------------------------- \n");
        Handle(Callback_1);
        printf(" \n");
        Handle(Callback_2);
        printf(" \n");
        Handle(Callback_3);
        printf("--------------------------------------------------- \n");
        return 0;
    }
    /*
    --------------------------------------------------- 
    Entering Handle Function. 
    Hello, this is Callback_1
    Leaving Handle Function.
    
    Entering Handle Function.
    Hello, this is Callback_2
    Leaving Handle Function.
    
    Entering Handle Function.
    Hello, this is Callback_3
    Leaving Handle Function.
    ---------------------------------------------------
    */
    
    • 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

    2、函数指针数组作为转移表
    为了使用switch语句,表示操作符的代码必须是整数。如果它们是从零开始连续的整数,我们可以使用转换表来实现相同的任务。转换表就是一个函数指针数组。这样方便把具体操作和选择操作的代码分开。

    # include 
    # include 
     
    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 a=8, b=5;
    	int ret = 0;
    	int(*p[5])(int a, int b) = {Add,Sub,Mul,Div};//函数指针数组的使用
        for(int i=0;i<4;i++){
            ret = (*p[i])(a, b);
            printf("ret=%d\n", ret);
        }
    	// system("pause");
    	return 0;
    }
    /*
    ret=13
    ret=3
    ret=40
    ret=1
    */
    
    • 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

    指向函数指针数组的指针 :void (*(p)[10])(const char);

    二维指针数组&数组指针

    二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的。

    int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
    /*
    0X61FDE0 0X61FDE4 0X61FDE8 0X61FDEC 0X61FDF0 0X61FDF4 0X61FDF8 0X61FDFC 0X61FE00 0X61FE04 0X61FE08 0X61FE0C
    	0		1		2			3		4		5		6			7		8		9		10			11		
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5

    int (*p)[4]为数组指针,p 指向的数据类型是int [4],那么p+1就前进 4×4 = 16 个字节,p-1就后退 16 个字节,这正好是数组 a 所包含的每个一维数组的长度。也就是说,p+1会使得指针指向二维数组的下一行,p-1会使得指针指向数组的上一行。

    int (*p)[4] = a;
    
    • 1

    数组名 a 在表达式中也会被转换为和 p 等价的指针!那么等价关系如下:
    a+i == p+i
    a[i] == p[i] == *(a+i) == *(p+i)
    a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == ((a+i)+j) == ((p+i)+j)

    #include 
    
    int main(){
        int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
        int(*p)[4];
        int i,j;
        p=a;
        for(i=0; i<3; i++){
            for(j=0; j<4; j++) printf("%2d  ",*(*(p+i)+j));
            printf("\n");
        }
        return 0;
    }
    
    
    /*
     0   1   2   3  
     4   5   6   7
     8   9  10  11
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    https://www.cnblogs.com/suwen/p/5266227.html
    http://c.biancheng.net/view/335.html
    https://www.runoob.com/w3cnote/c-callback-function.html
    https://blog.csdn.net/qq_29567701/article/details/83659742
    http://c.biancheng.net/view/2022.html

  • 相关阅读:
    好的期货公司开户让人省心省钱
    定时任务调度(crond)
    RabbitMQ运维-持久化机制和内存磁盘的监控
    网站登录界面制作(three.js 3D特效背景)+ boostrap导航栏实现 + jQuery移动窗口【附加源代码】
    【学习笔记】《深入浅出Pandas》第17章:Pandas实战案例
    Linux之安全最佳做法(未完成)
    漫谈:编码、哈希、摘要、加密都是什么(别再问“用base64加密行不行”了,会被鄙视)
    FPGA设计时序约束六、设置最大/最小时延
    技术分享| anyRTC音视频混流技术解析
    美容院圣诞节促销活动方案
  • 原文地址:https://blog.csdn.net/LW_12345/article/details/127648706