C语言笔记之指针(六)

C语言笔记之指针

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.指针总结

1.指针的引入

指针==地址,指针就是地址,地址就是指针

变量四种概念:

  • 类型
  • 变量名
  • 内存地址

例:

int data = 80;
//类型: int ;变量名:data ;值:80 ;内存地址 4
#include <stdio.h>
int main(){
	 printf("win 10系统下int类型的大小为:%d",sizeof(int));
        return 0;
}

运行结果:在这里插入图片描述

内存地址大小因操作系统而异。

一个变量访问可以通过变量名访问,也可以通过地址的方式访问。

#include <stdio.h>

int main()
{
    int data = 80;
    printf("data的值为:%d\n",data); //通过变量名访问值
    printf("data的地址为:0x%p\n",&data);	//&:取地址符  0x:16进制呈现
    printf("data的值为:%d\n",*(&data)); //通过地址访问值,*取值运算符,把后面跟的内存地址的数据取出来
    return 0;
}

运行结果:在这里插入图片描述

2.指针变量的引入

指针变量==存放地址的变量

*标识作用:只产生在指针变量定义或声明的时候。

*运算作用:取地址内容的时候。

#include <stdio.h>

int main()
{
    int data = 80;
	int *p;//这里的*是一个标识符,告诉系统我是一个指针变量,是用来保存别人地址的,和下方的运算服不同
	p = &data; //给指针变量赋值
    printf("变量名访问data的值为:%d\n",data); //通过变量名访问值
    printf("data的地址为:0x%p\n",&data);	//&:取地址符  0x:16进制呈现
    printf("地址访问data的值为:%d\n",*(&data)); //通过地址访问值,*取值运算符,把后面跟的内存地址的数据取出来
	printf("指针变量访问data的值为:%d\n",*p); //通过指针变量间接访问
    return 0;
}

运行结果:在这里插入图片描述

3.指针变量为什么要求类型

通过结果看出差别:

#include <stdio.h>

int main()
{
    
    int a = 0x1234;
    int *p = &a;
    char *c = &a;
    
    printf("p = %p\n",p);
    printf("c = %p\n",c);
    
    
    printf("a = %x\n",*p);//取值的时候出了问题,取值运算符会根据指针变量类型,访问不同大小的空间
    printf("a = %x\n",*c);
    
    
    printf("++p = %p\n",++p);
    printf("++c = %p\n",++c);
    return 0;
}

运行结果:在这里插入图片描述

红色方框内为警告:警告原因是不兼容的指针类型’int *'初始化’char * ,也就是为什么要求指针变量类型要一致的原因。指针变量类型要一致:决定指向内存空间的大小,也决定增量的大小。

4.指针场景一

传递实参地址到形参内存空间,通过*访问实参地址内容,进而实现交换两数

#include <stdio.h>
void chageData(int *pdata1,int *pdata2)
{
    int temp;
    temp = *pdata1;
    *pdata1 = *pdata2;
    *pdata2 = temp;
}

int main()
{
    int data1 = 2;
    int data2 = 8;
    printf("data1 = %d,data2 = %d\n",data1,data2);
    chageData(&data1,&data2);        //传地址
    printf("交换data1 = %d,data2 = %d\n",data1,data2);
    return 0;
}

运行结果:在这里插入图片描述

5.指针场景二

指向固定的区域


#include <stdio.h>

int main()
{
    int a = 10;
    printf("address of a is 0x%p\n",&a);
    volatile unsigned int *p = (volatile unsigned int *)0x0000000000610000;//volatile:防止编译器优化 ;unsigned int:无符号整形数
    
    printf("p = 0x%p\n",p);
    return 0;
}

运行结果:在这里插入图片描述

6.指针回顾&小练习

一、

  1. 指针是什么?

    ​ 指针等同于地址。

  2. 指针变量是什么?

    ​ 存放地址的变量

二、

*标识作用:只产生在指针变量定义或声明的时候。

*运算作用:取地址内容的时候。

三、

​ 变量名的两种访问方式:

	1. 直接访问(变量名访问)
	2. 间接访问(*取地址访问)

四、

​ 指针讲解内存大小:指针变量类型要一致:决定指向内存空间的大小,也决定增量的大小。 不同类型跨度不一样。

五、

​ 为什么要用指针?

​ 例如场景一,通过指针的方式,传递地址,修改参数。

小练习:

输入三个数a,b,c;要求不管怎么输入,输出时从大到小的顺序输出。(使用函数封装)

#include <stdio.h>
void changDataThree(int *data1,int *data2,int *data3)
{
	int max[3] = {0};
	max[0] = *data1;
	max[1] = *data2;
	max[2] = *data3;
	
	int i;
	int j;
	for(i = 0; i < 3;i++){
		printf("%d",max[i]);
	}
	int maxnum = 0;
	for(i=0;i < 2;i++){
		for(j=0;j<2-i;j++){
			if(max[j] < max[j+1]){		
				maxnum=max[j+1];
				max[j+1]=max[j];
				max[j] = maxnum;
			}
		}
	}
	*data1=max[0];
	*data2=max[1];
	*data3=max[2];
}
int main()
{
	int data1;
	int data2;
	int data3;
	
	printf("请输入第一个数:\n");
	scanf("%d",&data1);
	printf("请输入第二个数:\n");
	scanf("%d",&data2);
	printf("请输入第三个数:\n");
	scanf("%d",&data3);
	
	changDataThree(&data1,&data2,&data3);
	printf("从大到小输出结果:%d %d %d",data1,data2,data3);
    return 0;
}

运行结果:在这里插入图片描述

7.定义一个指针变量指向数组

定义一个指针变量指向一个数组元素

int data[5]={8,46,15,3,45};	//定义data为包含5个整型数据的数组
int *p;				//定义p为指向整型变量的指针变量
p = &data[0];		//把data[0]元素的地址赋给指针变量p
//指向数组首元素的地址

p = &data[0];	//p的值是data[0]的地址
p = data;		//p的值是数组data首元素(即data[0])的地址
//等于数组名:指向数组起始位置

测试:

#include <stdio.h>

int main()
{
    int array[3]={45,98,69};
    int *p;
    //p = &array[0];
    p = array;    //数组名 就是第一个元素的地址;
    printf("array的地址:%p\n",p);
	printf("首元素是:%d\n",*p);
    return 0;
}

运行结果:在这里插入图片描述

8.指针偏移遍历数组

#include <stdio.h>

int main()
{
    int array[3]={45,98,69};
    int *p;
    //p = &array[0];
    p = array;    //数组名 就是第一个元素的地址;
    printf("array的地址:%p\n",p);
	printf("第一个元素是:%d\n",*p);
    printf("第二个元素是:%d\n",*(p+1));//*(p+1) : p+1:不是地址+1,而是地址偏移了一个类型(在这里是int类型,也就是4个字节)的字节数 ,再取p偏移											//一个类型的字节数后地址的内容
    printf("第三个元素是:%d\n",*(p+2));//p+2 偏移两个类型(在这里是int类型,也就是4个字节)字节数
    return 0;
}

运行结果:在这里插入图片描述

char *p时:

#include <stdio.h>

int main()
{
    char array[3]={'d','g','p'};
    char *p;
    //p = &array[0];
    p = array;    //数组名 就是第一个元素的地址;
    printf("array的地址:%p\n",p);
	printf("第一个元素是:%c\n",*p);
    printf("第二个元素是:%c\n",*(p+1));//*(p+1) : p+1:不是地址+1,而是地址偏移了一个类型(在这里是char类型,也就是1个字节)的字节数 ,再取p偏移											//一个类型的字节数后地址的内容
    printf("第三个元素是:%c\n",*(p+2));//p+2 偏移两个类型(在这里是char类型,也就是1个字节)字节数
    return 0;
}

运行结果:在这里插入图片描述

指针遍历数组:

#include <stdio.h>

int main()
{    
    int arr[3] = {3,4,5};
    int *p;
    p = arr;
    int i;
	for(i = 0; i < 3; i++){
        printf("地址:0x%p ,值:%d\t",&arr[i],arr[i]);
       
    }
	puts("");
    for(i = 0; i < 3; i++){
        printf("地址:0x%p ,值:%d\t",(p+i),*(p+i));
        //printf("%d\t",*p++); //*优先级>++,先取内容再++;
        /*
            
        printf("%d\t",*p);
        p++;
            
        */
    }

    return 0;
}

运行结果:在这里插入图片描述

char类型时:

#include <stdio.h>

int main()
{    
    char arr[3] = {'b','g','m'};
    char *p;
    p = arr;
    int i;
	for(i = 0; i < 3; i++){
        printf("地址:0x%p ,值:%c\t",&arr[i],arr[i]);
       
    }
	puts("");
    for(i = 0; i < 3; i++){
        printf("地址:0x%p ,值:%c\t",(p+i),*(p+i));
        //printf("%d\t",*p++); //*优先级>++,先取内容再++;
        /*
            
        printf("%d\t",*p);
        p++;
            
        */
    }

    return 0;
}

运行结果:在这里插入图片描述

上面两个案列比较可以看出:指针变量类型要一致的重要性!按住Ctrl点我:四

访问数组元素:

  • 下标法(更简单,容易理解)

  • 指针法:通过指针引用数组:偏移,取内容 (容易出错)

在正确访问时:指针法访问数组效率高于下标法,

9.指针偏移的补充,重新赋值数组首地址

指针偏移的要点,记得重新赋数组首地址

#include <stdio.h>

int main()
{    
    int arr[3] = {3,4,5};
    int *p;
    p = arr;
    int i;
    for(i = 0; i < 3; i++){
        printf("%d\t",*(p+i));
        //printf("%d\t",p[i]); //p存放的是arr的地址,用地址去遍历
      }
    p = arr;    //重新赋数组首地址,不然会造成数组的越界
    for(i = 0; i < 3; i++){
        printf("%d\t",*(p+i));
      }
    return 0;
}

10.指针和数组名的一些写法!!!

#include <stdio.h>

int main()
{    
    int arr[3] = {3,4,5};
	char arr2[3] = {'b','g','m'};
    int *p;
	char *p2;
    p = arr;
    int i;
    printf("%d\n",*arr);
    printf("%d\n",*p);
    for(i = 0; i < 3; i++){
        //printf("%d\t",*(p+i));
        printf("%d\t",*p++);    //p是指针变量,存放是的是arr[3]数组的首地址
    }
    putchar('\n');
    p = arr;  //重新定义到首地址
    //   区别  
    for(i = 0; i < 3; i++){
        printf("%d\t",*p++);     //指针变量,保存地址的变量,保存的地址可变
    }
	 putchar('\n');
    for(i = 0; i < 3; i++){
        printf("%d\t",*(arr+i));
       //错误写法 printf("%d\t",*arr++);        //arr是常量指针,不能++,arr表示该数组首地址固定位置
    }
    putchar('\n');
    printf("sizeof arr is %d\n",sizeof(arr)); //int类型 有三个元素   4*3 = 12
    printf("sizeof arr is %d\n",sizeof(p));	 //操作系统用8个字节表示一个地址
	printf("int :sizeof arr is %d\n",sizeof(int ));
	printf("int *:sizeof arr is %d\n",sizeof(int *));
	printf("char :sizeof arr is %d\n",sizeof(char ));
	printf("char *:sizeof arr is %d\n",sizeof(char *));
	printf("sizeof arr is %d\n",sizeof(arr2));
	printf("sizeof arr is %d\n",sizeof(p2));
    return 0;
}

运行结果:在这里插入图片描述

由此可见:在win10操作系统下,只要是指针就是8个字节

11.练习函数指针数组结合

函数指针数组

#include <stdio.h>
//name , params, returnValue
void initArray(int *parr, int size)
{
    int i;
    for(i=0;i<size;i++){
        printf("请输入第%i个元素的数据:\n",i+1);//%i 可以匹配八进制、十进制、十六进制表示的整数。
                                              //如若输入的数字有前缀 0,%i将会把它当作八进制数来处理,
                                              //当然如若是前缀0x ,它将以十六进制来处理。
        scanf("%d",parr++);
    }
}
void printArray(int *parr, int size)
{
    int i;
    for(i=0;i<size;i++){
        printf("%d ",*parr++);
    }
}

int main()
{
    int arry[5];
    int size = sizeof(arry)/sizeof(arry[0]);
    
    initArray(arry,size);//实际参数,数组的首地址: 名,首个元素的地址
    printArray(&arry[0],size);
    return 0;
}

运行结果:在这里插入图片描述

12.练习数组翻转

数组翻转

#include <stdio.h>
void printfArr(int *parr,int size)
{
    int i;
    for(i = 0; i < size; i++){
        printf("%d\t",*parr++);
    }    
}
void mirrorArr(int *parr,int size)    //翻转数组(镜像)
{
    int i;
    int j;
    int temp;
    for(i = 0; i < size/2;i++){
        j = size-1-i;
        temp = *(parr+i);
        *(parr+i) = *(parr+j);
        *(parr+j) = temp;
    }
    for(i = 0 ; i < size ; i++){
        
        printf("%d\t",*parr++);
    }
    
}
int main()
{
    int arr[] = {1,2,3,4,5};
    int size = sizeof(arr)/sizeof(arr[0]);
    printfArr(arr,size);
    putchar('\n');
    mirrorArr(arr,size);
    return 0;
}

运行结果:在这里插入图片描述

13.二维数组的地址认知一

在这里插入图片描述
数组名=地址

父子数组

行:父数组:a

列:子数组:a[0] a[1] a[2]

a[0] :是元素 1 3 5 7 的数组名

a[1] :是元素 9 11 13 15的数组名

a[2] :是元素17 19 21 23 的数组名

printf("%d\n",a+1);// a+1 :9

printf("%d\n",a[0]+1);// a+1 :3

二维数组本质还是数组,不通电是数组元素还是个数组(子数组)

14.二维数组的地址认知二

&a[0][0]= a[0];//a[0] 是数组名 a[0][0]首元素的地址

&a[1][0]= a[1];//a[1] 是数组名 a[1][0]首元素的地址

&a[2][0]= a[2];//a[2] 是数组名 a[2][0]首元素的地址

*a = *(a+0)

a[0] = *(a+0) = &a[0][0]

a[0]+1 =*(a+0)+1 = &a[0][1]

15.编程验证二位数组的地址

arr是二维数组,对二维数组取内容,每一子内容元素都是数组,计算机对数组操作不是操作找那个个数组,而是操作数组首地址

#include <stdio.h>
int main()
{
    int arr[3][4] ={
					{25,26,44,84},
					{94,64,1,64},
					{64,14,9,4}
				};
	int i;
	int j;
	for(i = 0;i < 3;i++)
	{
		for(j = 0;j < 4;j++){
			printf("arr[%d][%d]的地址:%p\t值:%d\t",i+1,j+1,&arr[i][j],arr[i][j]);
			
		}
		puts("");
	}
	puts("");
	printf("arr是父亲地址:                  %p\t偏移1后是%p\n",arr,arr+1);
	printf("arr[0]是子地址:                 %p\t偏移1后是%p\n",arr[0],arr[0]+1);
	printf("arr[0][0]是子数组a[0]中的首地址:%p\t偏移1后是%p\n",&arr[0][0],&arr[0][1]);
	printf("*(arr+0)是子数组(a[0])地址:     %p\t偏移1后是%p\n",*(arr+0),*(arr+0)+1);
	printf("(arr+0)是子数组(a[0])地址:      %p\t偏移1后是%p\n",(arr+0),(arr+0)+1);
	printf("*(arr+1)是子数组(a[1])地址:     %p\t偏移1后是%p\n",*(arr+1),*(arr+1)+1);
	printf("(arr+1)是子数组(a[1])地址:      %p\t偏移1后是%p\n",(arr+1),(arr+1)+1);
    return 0;
}

运行结果:在这里插入图片描述

16.二维数组的地址写法应用

二维数组应用:

#include <stdio.h>
int main()
{
    int arr[3][4] ={
					{25,26,44,84},
					{94,64,1,64},
					{64,14,9,4}
				};
	int i;
	int j;
	for(i = 0;i < 3;i++)
	{
		for(j = 0;j < 4;j++){
	
				printf("arr[0][0]是子数组a[0]中的首地址:0x%p\t值是%d\n",&arr[i][j],arr[i][j]);
				printf("arr[0][0]是子数组a[0]中的首地址:0x%p\t值是%d\n",arr[i]+j,*(arr[i]+j));
				printf("arr[0][0]是子数组a[0]中的首地址:0x%p\t值是%d\n",*(arr+i)+j,*(*(arr+i)+j));
		}
		puts("================================================================================");
	}

	
    return 0;
}

运行结果:在这里插入图片描述

17.数组指针

数组指针:定义一个指针,指向数组

#include <stdio.h>

//arr,arr[0]
int main()
{
    int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr+
    int i,j;
    int *p;//p++
//    p = &arr[0][0];
    //p = arr;
    //能不能定义一个指针,让指针偏移的时候,也偏移对应大小的数组?
    //数组指针,定义一个指针,指向一个数组!
    //数组指针才是真正等同于二维数组名
    int (*p2)[4];
    p2 = arr;
//    printf("p2=%p\n",p2);
//    printf("++p2=%p\n",++p2);
    for(i=0;i<3;i++){
        for(j=0;j<4;j++){
            printf("%d\n",*(*(p2+i)+j));
        }
        
    }
    return 0;
}

运行结果:在这里插入图片描述

18.数组指针和二维数组的配合应用

实现查找二维数组的数据:

#include <stdio.h>

int getTheData(int (*p)[4],int hang,int lie) //获取行列数据
{
    int data;
    data = *(*(p+hang)+lie);
    return data;
    //return p[hang][lie];
}
void tipsInputHangLie(int *pm, int *pn) //输入行列
{
    printf("输入行列值:\n");
    scanf("%d%d",pm,pn);
    puts("done!");
}
//arr,arr[0]
int main()
{
    int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr+
    int ihang,ilie;
    int data;
    
    //1. 提示用户输入行列值
    tipsInputHangLie(&ihang,&ilie);
    //2. 找出对应行列值的那个数
    data = getTheData(arr,ihang,ilie);
    //3. 打印出来
    printf("%d行%d列的值是%d\n",ihang,ilie,data);
}

运行结果:在这里插入图片描述

19.函数指针的认知

函数指针:函数地址

函数名就是地址

如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。

定义函数指针:

int sendData(int a;int b);
int (*p)(int a,int b);//函数指针

函数调用概念和变量一样

  • 直接访问:变量名(函数名)
  • 间接访问:指针(函数指针)

函数指针:

#include <stdio.h>
int addData(int data)
{
    return ++data;
}
void printfWelcome()
{

    printf("Welcome\n");
}
int main()
{
    void (*p)();	//定义函数指针变量
    int (*p2)(int data);
    printfWelcome();			
    p = printfWelcome;	//指向函数
    p2 = addData;
    (*p)();	//调用
    printf("%d\n",(*p2)(10));
    return 0;
    
    return 0;
}

运行结果:在这里插入图片描述

根据程序运行过程的不同情况,调用不同的函数。

20.函数指针–回调函数

函数指针–回调函数

#include <stdio.h>
#include <stdlib.h>
int addData(int data1, int data2)
{
    
    return data1+data2;
}
int subData(int data1, int data2)
{
    
    return data1-data2;
}
int retData(int data1, int data2,int (*p)(int ,int)) // 类型很重要,  名是访问方式
{
    int ret;
    ret = (*p)(data1,data2);
    return ret;
}

int main()
{
    int ret;
    int cmd = 0;
    int data1 ;
    int data2 ;
    int (*p)(int data1,int data2);
    printf("请输入第一个数\n");
    scanf("%d",&data1);
    printf("请输入第二个数\n");
    scanf("%d",&data2);

    printf("请选择指令:1,加法;2,减法\n");
    scanf("%d",&cmd);
    switch(cmd){
        case 1: p = addData;
                printf("add\n");
                break;
        case 2: p = subData;
                printf("add\n");
                break;
        default: printf("请输入正确指令\n");
                exit(-1);
                break;
    }
    ret = retData(data1,data2,p);
    printf("%d\n",ret);
    return 0;
}

运行结果:在这里插入图片描述

21指针数组的概念和实现

指针数组定义:

int *p[4]; //指针数组,,元素均为指针类型数据。每一个元素都是存放一个地址,相当于指针变量。
int (*p)[4];//数组指针

指针数组:

#include <stdio.h>

int main()
{
    int a = 4;
    int b = 5;
    int c = 6;
    int d = 7;
    int i;
    int *p[4] = {&a,&b,&c,&d};
    for(i = 0; i < 4;i++){
        printf("%d\t",*(p[i])); //[] 比 * 优先级高,所以p先与[4]结合 ,建议还是写上小括号方便阅读
    }

    return 0;
}

运行结果:在这里插入图片描述

函数指针数组

#include <stdio.h>
#include <stdlib.h>
int addData(int data1, int data2)
{
    
    return data1+data2;
}
int subData(int data1, int data2)
{
    
    return data1-data2;
}
int retData(int data1, int data2,int (*p)(int ,int))
{
    int ret;
    ret = (*p)(data1,data2);
    return ret;
}

int main()
{
    int cmd = 0;
    int data1 ;
    int data2 ;
    int i;
    int ret;
    int (*p)(int data1,int data2); //函数指针
    printf("请输入第一个数\n");
    scanf("%d",&data1);
    printf("请输入第二个数\n");
    scanf("%d",&data2);
    int (*p3[2])(int ,int) = {addData,subData};  //int (*p3[2])(int ,int)  函数指针数组 ,存放同类型(同形参)的函数地址(函数名)
    for(i = 0; i < 2;i++){
        ret = (*p3[i])(data1,data2);
        printf("ret = %d\n",ret);
    }
    
    return 0;
}

运行结果:在这里插入图片描述

22.指针函数的的概念和实现

在这里插入图片描述

概念:

​ 一个函数可以返回一个整型值、字符值、实型值(小数)等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。
​ 例如“inta(intx,inty);”,a是函数名,调用它以后能得到一个int型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。
​ 请注意在a两侧没有括号,在a的两侧分别为运算符和()运算符。而()优先级高于,因此a先与()结合,显然这是函数形式。这个函数前面有一个*,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。

int *p;//定义一个指针变量
int* p;//定义一个int类型指针类型变量

函数指针:返回指针的函数

指针在意的起始地址偏移地址

指针函数

#include <stdio.h>
#include <stdlib.h>
int *pfuns(int number,int (*p)[4])
{
    int *p2;
    p2 = (int *)(p+number);
    printf("(int *)p地址:%p\n",(int *)p);
    printf("(int *)(p+number)地址:%p\n",(int *)(p+number));
    printf("(int *)p+number地址:%p\n",(int *)p+number);
    return p2;
}
int main()
{    
    int arr[3][4]={
        {45,86,94,78},
        {78,91,84,21},
        {54,56,98,62}
    };
    int i;
    int (*p)[4];
    int number;
    int ret;
    int *studentNumber;
    p = arr;
    puts("请输入想看第几个学生的成绩");
    scanf("%d",&number);
    studentNumber = pfuns(number,p); //二维数组地址传回来后,地址给了int *studentNumber,此时应看为子数组
	
    for(i = 0; i < 4; i++){
        printf("%d\t",*studentNumber++);//*studentNumber++,是访问子数组的元素数据
    }
    return 0;
}

运行结果:在这里插入图片描述

23.二级指针的认知

二级指针

#include <stdio.h>

int main()
{
    int data = 10;
    int *p = &data;
    printf("data的值:%d\n",data);
    printf("data的地址:%p\n",&data);
	printf("p自己的地址:%p\n",&p);
	printf("p保存的是data的地址:%p\n",p);
    printf("*p:取p保存地址的值:%d\n",*p);
 	
    printf("===========================================\n");
    int **p2 = &p; //二级指针
	printf("p2自己的地址:%p\n",&p2);
    printf("p2保存的是p的地址:%p\n",p2);
    printf("*p2访问p的内容:%p\n",*p2); //*p2 访问p的内容(也就是p保存data的地址)
    printf("**p2:取p保存的内容的值:%d\n",**p2); //*p2:取p2地址保存的地址的值  p2 = &p;也就是取p保存的地址的内容(data的数据)
     printf("===========================================\n");
    int ***p3;
    p3 = &p2;

    printf("p3保存的是p2的地址:%p\n",p3);
    printf("*p3:访问p2的内容(也就是p的地址):%p\n",*p3);    //p2的地址
    printf("**p3:访问p2内容的值:%p\n",**p3);    //也就是data的地址
	printf("***p3:访问p2内容的值的值:%d\n",***p3);    //也就是data的数据
    return 0;
}

运行结果:在这里插入图片描述

24.二级指针的应用

应用:

#include <stdio.h>
#include <stdlib.h>
void pfuns(int number,int (*p)[4],int **studentNumber) // 类似int *p2;int **p = &p2; 操作p2保存的地址
{

    *studentNumber = (int *)(p + number);

}
int main()
{    
    int arr[3][4]={
        {45,86,94,78},
        {78,91,84,21},
        {54,56,98,62}
    };
    int i;
    int (*p)[4];
    int number;
    int ret;
    int *studentNumber;
    p = arr;
    puts("请输入想看第几个学生的成绩");
    scanf("%d",&number);
    pfuns(number,p,&studentNumber); //二维数组地址传回来后,地址给了int *studentNumber,此时应看为子数组
	
    for(i = 0; i < 4; i++){
        printf("%d\t",*studentNumber++);//*studentNumber++,是访问子数组的元素数据
    }
    return 0;
}

运行结果:在这里插入图片描述

25.二级指针和二维数组的闭坑指南

以下工作中不会使用:

#include <stdio.h>
#include <stdlib.h>
#define SQR(X) X * X 
int main()
{    
	int arr[3][4]={
		{15,45,54,58},
		{15,124,51,45},
		{48,1940,6,656,}
	};//int (*p)[4]
	int (*p2)[4] = arr;
	
	int **p ;
	p = &p2;
	**p = 666;
	printf("%d\n",arr[0][0]);
	/*
	p = arr;
	printf("arr:%p\n",arr);

	printf("p:%p\n",p);
	printf("*p:%p\n",*p);//*p是野指针,不是哦我们认为的会变成列的地址
	*/
    return 0;
}	

运行结果:在这里插入图片描述

警告是因为类型不匹配!

26.指针总结

各种指针的定义:

1.一个整形数:int a;
2.一个指向整形数的指针:int *a;
3.一个指向指针的指针,它指向的指针指向一个整形数:int **a;
4.一个有10个整形数的数组:int a[10];
5.一个有10个指针的数组,每个指针指向一个整形数:int *a[10];
6.一个指向有10个整形数的数组的指针:int (*a)[10];
7.一个指向指针的指针,被指向的指针指向一个有10个整形数的数组:int (**a)[10];
8.一个指向数组的指针,改数组有10个整形指针:int *(*a)[10];
9.一个指向函数的指针,该函数有一个整形参数并返回一个整数:int (*a)(int);
10.一个有10个指针的数组,每个指针指向一个函数,该函数有一个整形参数并返回一个整形数:int (*a[10](int));
11.一个函数的指针,指向的函数的类型是有两个整形参数并且返回一个函数指针的函数,
   返回的函数指针指向又一个整形参数且返回整形数的函数:int(*(*a)(int,int))(int)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值