指针的那些事!

指针引入:

开胃小菜:

定义一个变量,然后将其输出(新内容指针引入)

// 指针  == 地址,地址就是指针,指针就是地址。
// 变量包含四种概念:int(类型)  a(变量名) (内存地址)= 10(值)
#include <stdio.h>


// 什么是整型变量,存放整型数的变量
// 什么是字符变量,存放字符的变量
// 什么是指针变量,存放指针的变量
// 什么是指针变量,存放地址的变量
int main()
{
    int a = 10 ;
    int *b;   //这里的*是一个标识符,告诉系统我是一个指针变量,是用来保存别人地址的变量,不是取值运算,
    b = &a; 
    printf("a = %d \n",a);
    printf("a的地址是: %p \n",&a);
    printf("a的地址是: %p \n",b);  // 通过指针访问地址

    printf("a地址访问: %d \n",*(&a));  //  *取值运算符,他把后面跟的内存地址中的数据“取出来”。
    printf("指针变量的访问方式 %d \n",*b);   //通过指针取值

    return 0 ;

}

由此可见,指针 = 地址 地址= 指针

指针变量和其他类型的变量一样,也有四要素:

变量名、类型、(存储空间)地址、值

有关指针变量注意事项:

① 定义指针变量要区分其类型;(详情可看代码注释)

② 定义指针变量时标识符*的应用(标识符只产生在指针变量定义或申明的时候,与运算符不同);

③ 取地址符号&;

④ 打印指针变量时取值运算符*的应用;

⑤ 指针变量作为一个变量,也有它自己的地址;

指针类型:

代码验证:

#include <stdio.h>
//指针类型:

int main()
{
	int a = 0x1234;// 以十六进制的方式 int 4个字节  char 1个字节  一个字节等于8位
	int *b = &a; 
	char *c = &a;	
	
	// 打印b、c 的地址。  地址一样
	printf("b = %p \n",b);
	printf("c = %p \n",c);
	
	// 以十六进制方式打印 b、c。
	
	printf("a = %x\n",*b);  //这里输出是全部输出,因为a是int 的类型,  int是4个字节   一个字节8位   32 可以全部访问完毕
	printf("a = %x\n",*c);  // 这里输出只能输出34,因为char是一个字节, 一个字节8位    8位只能访问34.
	
    /*取值运算符会根据指针变量类型,访问不同大小的存储空间。
	区分指针变量类型不仅能够决定指向空间的大小,而且还能决定增量。*/
	
	printf("a的地址加1是:%p\n",b+1);  // 地址自增
	printf("a的地址加1是:%p\n",c+1);

	
	return 0 ;
	
}

取值运算符会根据指针变量类型,访问不同大小的存储空间。区分指针变量类型不仅能够决定指向空间的大小,而且还能决定增量。

指针作用:

编程练习:封装一个函数,实现两个数的交换

当使用函数调用的时候,形参里面的变量是临时地址,在形参里不管如何操作,其结果不会传到main函数中,因为不是同一个地址。

/*#include <stdio.h>

//  输入2个数a,b,; 要求不管怎么输入,在输出的时候,a,b就是由大到小的顺序输出,用函数封装实现

void inArry(int a,int b)
{
	int tmp;
		tmp = a;
		a = b;
		b = tmp;  	
		
}
// 这里的变量名 与main函数的变量名不是同一个地址,
// inArry中的变量名地址是临时地址。

int main()
{
	int a,b;
	printf("请输入两数\n");
	
	scanf("%d%d",&a,&b);
	
	
	printf("a= %d\n",a);	
	printf("a= %d\n",b);	
	
	inArry(a,b);
	printf("交换之后的a = %d,d = %d\n",a,b);
	
		
	return 0;
}*/

#include <stdio.h>
                    //把*pdata1(指针) 当成一个变量去看    
void ChangeData(int *pdata1,int *pdata2,int *pdata3)
{
	int temp;
	if(*pdata1 < *pdata2){
	temp = *pdata1;
	*pdata1 = *pdata2;
	*pdata2 = temp;
	}
	 if(*pdata1 < *pdata3){
	temp = *pdata1;
	*pdata1 = *pdata3;
	*pdata3 = temp;
	
	}
	if (*pdata2 < *pdata3){
	temp = *pdata2;
	*pdata2 = *pdata3;
	*pdata3 = temp;
	}
}
// 通过指针传的相同地址,所以会交换成功。
int main()
{
	int data1 ,data2,data3;
	
	puts("原样输入");
	scanf("%d%d%d",&data1,&data2,&data3);
	printf("data1 = %d data2 = %d data2 = %d\n",data1,data2,data3);
	
	ChangeData(&data1,&data2,&data3);  //传地址。
	
	puts("排序后输出");
	printf("data1 = %d data2 = %d data3 = %d\n",data1,data2,data3);
	
	return 0;
}


指针变量_通过指针指向数组: int *p

定义了一个指针变量:

int *p ;指针变量

对p 取值 就是 通过指针,对第0个元素的地址等于p

什么是指针变量 -就是保存地址的变量。

int a【10】;

p = & a【0】 // 下标法,第0个元素的地址等于p

编程练习:

#include <stdio.h>
// 指针_变量
int main()
{
	int arry[10]= {1,5,7,8,9,11,25,14,16,24};
	
	int *p ;
	p = &arry[0];   //p的值是a[0]的地址;数组的首地址就是首个元素的地址
	
	
	// p 的值是数组a的名称 就是数组的首地址
	p = arry ;
	
	printf("第0个元素是:%d\n" ,*p);     //打印值 
	printf("第0个地址是:%p\n" ,p);     // 打印地址
	
    // 指针地址延申(偏移)
    printf("第1个元素是:%d\n" ,*(p+1));   // 地址向后延申4个字节  5
	printf("第2个元素是:%d\n" ,*(p+2));  // 地址向后延申8个字节   7
	printf("done\n");
	// 通过for循环进行 向后延申
	int i ;
	for(i = 0 ;i<3;i++){
	//	printf("第%d个元素是:%d\n" ,i,*(p+i));
		//printf("第%d个元素是:%d\n" ,i,*p++); //先*p,在++。
		
		printf("第%d个元素是:%d\n" ,i,*p); //先*p,在++。
		p++;
	}
	//注意 有一个问题,同样的代码再来一次,就会出现地址错误,
	p = arry ;
	   // 将指针地址重新回到首元素。就可以解决地址错误。
     //数组初始化!!!当使用指针偏移来访问数组时,要注意指针是否越界
	for(i = 0 ;i<3;i++){
	//	printf("第%d个元素是:%d\n" ,i,*(p+i));
		//printf("第%d个元素是:%d\n" ,i,*p++); //先*p,在++。
		
		printf("第%d个元素是:%d\n" ,i,*p); //先*p,在++。
		p++;
	}
	return 0;
	
}

见怪不怪 :指针下标法
#include <stdio.h>
// 指针地址见怪不怪  面试题
int main()
{
	int arr[3] = {1,0,3};
	
	int *p ;    // *p 是指针变量, arr数组是常变量
	p = arr;   // == p = &arr[0] 下标法
	
	
	
	// 指针通过下标法,访问指定的数组元素,如p[2]
	printf("数组第3位的元素是:%d\n",p[2]);
    for(int i = 0;i <3;i++){
		printf("数组第%d的元素是:%d\n",i,p[i]); // 通过下标法,访问指定的数组元素。
	}
	
    return 0;
}
	
见怪不怪 :数组名
#include <stdio.h>
// 指针地址见怪不怪  面试题
int main()
{
	int arr[3] = {1,0,3};
	
	int *p ;    // *p 是指针变量, arr数组是常变量
	p = arr;   // == p = &arr[0] 下标法
	
	
	
	// 数组名通过下标法,访问指定的数组元素,如arr[2]
	printf("数组第3位的元素是:%d\n",arr[2]);
    for(int i = 0;i <3;i++){
		printf("数组第%d的元素是:%d\n",i,arr[i]); // 通过下标法,访问指定的数组元素。
	}
	
    return 0;
}
	
见怪不怪 :指针
#include <stdio.h>
// 指针地址见怪不怪  面试题
int main()
{
	int arr[3] = {1,0,3};
	
	int *p ;    // *p 是指针变量, arr数组是常变量
	p = arr;   // == p = &arr[0] 下标法
	
	
	
	// 数组名通过下标法,访问指定的数组元素,如arr[2]
	printf("数组第3位的元素是:%d\n",*(p+3));
    for(int i = 0;i <3;i++){
		printf("数组第%d的元素是:%d\n",i,*(p+i)); // 通过下标法,访问指定的数组元素。
	}
	
    return 0;
}
	
数组名++??

arr++ 数组名指针常量 是不可以的++的 指针地址++ 可以

*p 是保存地址的变量 ,你可以的变量进行操作,不能对指针常量操作。

printf("数组第%d的元素是:%d\n",i,*p++); // 先取地址 在加加。

#include <stdio.h>
// 指针地址见怪不怪  面试题
int main()
{
	int arr[3] = {1,0,3};
	
	int *p ;    // *p 是指针变量, arr数组是常变量
	p = arr;   // == p = &arr[0] 下标法
	
	for(int i = 0;i <3;i++){  //arr++ 数组名指针常量 是不可以的  指针地址++ 可以
	                         // *p 是保存地址的变量  ,你可以的变量进行操作,不能对指针常量操作。
		printf("数组第%d的元素是:%d\n",i,*p++);  // 先取地址 在加加。
	}
	
	return 0;
}
sizeof地址大小不一样
#include <stdio.h>
// 指针地址见怪不怪  面试题
int main()
{
	int arr[3] = {1,0,3};
	
	int *p ;    // *p 是指针变量, arr数组是常变量
	p = arr;   // == p = &arr[0] 下标法
	
	// sizeof(数组名) 表示整个数组用多少个字节, sizeof(指针变量)表示首个地址,在计算机内存当中首地址用多少个字节。
	printf("sizeof arr is %d \n",sizeof(arr));  // 12   arr 数组名 12个字节    一个字节 4位。   计算整个数组的大小
	printf("sizeof arr is %d \n",sizeof(p));  //8  指针首个地址 占8个字节  在本操作系统指针存放一个地址所用的8个字节。
	printf("sizeof arr is %d \n",sizeof(int));  //4  整型数 占4个字节  
	printf("sizeof arr is %d \n",sizeof(int*));  //8  int指针 本操作系统存放一个地址所用的8个字节
	printf("sizeof arr is %d \n",sizeof(char*));  //8 char 指针 本操作系统存放一个地址所用的8个字节


    return 0;
    }

指针和二维数组:

二维数组:父子数组

父数组 = a 行地址 a 是数组的名字,也是二维数组第0行的首地址。

a = a+0 表示在第0行

a+1 表示在第1行

子数组 = a[0] ,*a列地址 a[0]表示在第0列 也是第0列的首地址,

a[0]+0 表示第o行第0列

a[0]+1 表示第o行第1列

此处的a[0],a[1],a[2]中的0,1,2并不代表数组的大小,而代表数组的名字。

数组名就是数组的首地址(列地址),因此数组的首地址就是首个元素的地址,

思考:a是谁的地址,a[0]又是什么,那*a或者*(a+0)呢?

a是数组名 即父数组(行)首地址;

a[0]是子数组(列)首地址;

*a = *(a+1)=a[0]子数组(列)首地址;

原因是:*a相当于是取数组a里面的值,而二维数组里还有一维数组,c语言里没有直接操作数组的概念,操作的是数组首地址,操作的实际上是列地址。所以*a = a[0]

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

原因是:括号的优先级要高于取值符号(星号),所以先执行括号里的,即*(a+0) = *a = a[0])

即:a[0] (数组名)= *(a+0)(指针)= &a[0][0](下标);

a[0]+1(数组名) = &a[0][1](下标法)= *(a+1)(指针)

a[0]+1:是第0行第一列的地址,是地址的意思(相当于*(a+0)+1),也可以说是第0个子数组的第1个元素的地址,而第0个子数组的第1个元素表示方式是a[0][1]。

#include <stdio.h>
int main()
{
	int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,100,12,18}};
	
	printf("a的父地址是:%p,偏移1后的地址是:%p\n",a,a+1);
	printf("数组名:a[0]的子地址是:%p,偏移1后的地址是:%p\n",a[0],a[0]+1);
	printf("指针法:a[0]的子地址是:%p,偏移1后的地址是:%p\n",*(a+0),*(a+0)+1);
	printf("指针法取值:a[0]的子数值是:%d,偏移1后的地址是:%d\n",*(*(a+0)),*(*(a+0)+1));
	                                                //* a =a[0]  ,*a+1=a[0][1];
	return 0;
}

补充知识点:

基础:

进阶:

思考:

虽然a+1和*(a+1)的地址和值是相同,但它们所代表的意义不一样。

a+1是面向行的地址(父数组),*(a+1)代表的是面向列的地址(子数组)。

面试题:

代码展示:

#include <stdio.h>
// 二维指针数组
 
int main()
{
	int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,100,12,18}};
	int i,j;
	
	for(i=0;i<2;i++)
	{
		for(j=0;j<3;j++)
		{
			printf("address1: %p,value: %d\n",&a[i][j],a[i][j]);     // 下标法访问。
			                       
								          // a[i]+j 这是一个地址  对地址取值 加*
			printf("address2: %p,value: %d\n",a[i]+j,*(a[i]+j));
			
			    //虽然a+1和*(a+1)的地址相同,值相同,但它们所代表的意义不一样,a+1是面向行的地址,*(a+1)代表的是面向列的地址。
								   // (a+i)+j != a[i]+j ;*(a+i)+j )=a[i]+j
			printf("address3: %p,value: %d\n",*(a+i)+j,*(*(a+i)+j));  //
			printf("=======================================\n");
		}
	}
	return 0;
}

数组指针: int (*p)[4]

编程练习: 指向一个含有4个元素的数组

#include <stdio.h>
// 二维指针数组
int main()
{
	int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,100,12,18}}	
	
	int i,j;
	int *p;  // 子数组
	//p = &a[0][0]; // 下标   0行0列首地址 == p
	//p=arr;
	
	// 数组指针 定义一个指针,指向数组
	//数组指针 才是真正等同于二维数组名
	int (*p2)[4];   // 定义指向子数组的指针
    p2 = a;  // 指向父数组的指针
	
	for(i=0;i<3;i++)
	{
		for(j=0;j<4;j++)
		{                   
			printf(" %p\n",*(p2+i)+j);  //打印12元素的地址。
		
			printf(" %d\n",*(*(p2+i)+j));    // 打印元素的值
			
		}
	}
	printf("p2=%p,\n",p2);// 打印父数组的地址
	 //  先自增,在取值
    printf("p2=%p\n", ++p2);  // 打印自增后的父数组地址
    
	return 0;
	
}   

编程练习:输出二维数组任意行列的数


#include  <stdio.h>
void inArry(int *hang, int *lie ){
	printf("请输入行和列的值\n");
	
	scanf("%d%d",hang,lie);
	puts("doen!");
}
	
int petArry(int hang,int lie,int (*p)[4]){
	
	int data;
		data = *(*(p+hang) +lie);
		return data ;
}


int main()
{
	int hang, lie;
	int arry[][4]={
		{33,55,24,67},
			{66,88,98,77},
				{65,28,57,46},
					{18,26,57,44}};
	
	int *p;
	inArry(&hang,&lie);
	int over = petArry(hang,lie,arry);
	
	printf("第%d行,第%d列的数值是%d\n",hang+1,lie+1,over);
					
	return 0;
}

函数指针: int (*p)(int a , int b)

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

函数名就是地址,访问的时候能用函数名访问,也能通过函数指针访问(和数组指针一样)

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

如何定义一个函数指针变量:将函数整体复制下来,然后把函数名定义成指针变量。

例如:

定义有参数,有返回值类型的函数指针:int data(int a,int b);定义函数指针为int (*p)(int a,int b)后p = data;调用的时候直接调用(*p)(int a,int b);

int data(int a,int b);

p = data()

data(int a,int b); 正常函数调用法

(*p)(int a,int b); 函数指针调用法

定义无参数无返回值类型的函数指针:void data(int a,int b)定义函数指针为void (*p)()后p = data;调用的时候直接调用(*p)();

void data(int a,int b)

p = data()

data(int a,int b); 正常函数调用法

(*p)(int a,int b); 函数指针调用法

代码验证:

#include <stdio.h>
//函数地址(指针)
void print()
{
	puts("程序启动\n");
}

int includ(int data){
	 
	 data ++;
	 return data ;
}
int main()
{
	print();  //普通函数调用
	
	
    void (*p)();  // 定义一个指针函数变量(函数指针变量)
	p = print;   // 指向函数   
	
	(*p)();  // 指针函数调用 调用函数print	 (*p)指针 
	
	int a = 10;   //定义了一个整型 变量   变量名为a,值为10;
	int *pa;  //变量指针    对pa的地址取值
	pa =  &a;  // 将a的地址赋值给pa 
	printf("变量a = %d\n",*pa);
	
	int data =20;   //定义了一个整型 变量   变量名为data,值为20;
	int (*d)(int data);   // 定义了一个函数指针,并对函数调用传了个形参。
	
	d = includ;      // 函数 赋值给了d
	int over =(*d)(data);   // 调用函数includ,并传参
	printf("测试:%d",over);

	return 0;
}

编程练习:

例8.24 有两个整数a和b,由用户输人1,2或3.如输人1,程序就给出a和b中大者,输人2,就给出a和b中小者,输人3,则求a与b之和。

#include <stdio.h>
//函数地址
int  a = 20;
int b = 45;
 
int inarr(int a ,int b){
	
	return  a > b ?a :b ;
}

int petarr(int a ,int b){
	 
	return  a < b ?a :b ;
}
 
 int getarr(int a ,int b){
	
	return a + b ;
}
int main(){
	int  i ;
	printf("请输入一个值\n");
	scanf("%d",&i);
	
	if(i==1){
	
	
	int (*big)(int a,int b);
     
     big = inarr;
    
    (*big)(a,b);
    printf("两者之间的大数是%d",(*big)(a,b))	;
	
	}
	
	if(i==2){
	
	
	int (*loat)(int a,int b);
     
     loat = petarr;
    
    (*loat)(a,b);
	
    printf("两者之间的小数是%d",(*loat)( a,b));
	
	}
	
	if(i==3){
	
	
	int (*sum)(int a,int b);
     
     sum = getarr;
    
    (*sum)(a,b);
	
    printf("两者之间的和数是%d",(*sum)( a,b));
	
	}
	return 0;
}	

#include <stdio.h>
#include <stdlib.h>
 
int GetMaxData(int data1,int data2)
{
	return data1>data2?data1:data2;
}
 
int GetMinData(int data1,int data2)
{
	return data1<data2?data1:data2;
}
 
int GetSumData(int data1,int data2)
{
	return data1+data2;
}
 
int GetSubData(int data1,int data2)
{
	return data1-data2;
}
              // 定义函数:根据传入的函数指针计算最终结果
int GetlastData(int data1,int data2,int (*p)(int data1,int data2))
{
	int data;
	data = (*p)(data1,data2);   
	return data;
}

int main()
{	
	int data1 = 20;
	int data2 = 10;
	int cmd;
	int result;
	int (*p)(int data1,int data2);  // 定义了一个指向函数的指针变量。
	
	printf("Please Enter the number:1(max),2(min),3(sum),4(sub)\n");
	scanf("%d",&cmd);
	
	switch(cmd)
	{
		case 1:
			p = GetMaxData;
			break;
		case 2:
			p = GetMinData;
			break;
		case 3:
			p = GetSumData;
			break;
		case 4:
			p = GetSubData;
			break;
		default:
			printf("输入错误,请重新输入\n");
			exit(-1);
			break;
	}
	result = GetlastData(data1,data2,p);     // 调用 GetlastData 函数计算最终结果
	printf("result = %d\n",result);
	
	return 0;
}

指针数组: int * p[4]

就是数组里面所有元素都是一个指针。

注意:

指针函数

int * (p(int a,int b))是一个函数,它的返回值的类型是一个指针类型;

函数指针

int (*p)(int a,int b))是一个指针,它是定义一个指针变量指向一个函数。

例8.25 有a个学生,每个学生有b门课程的成绩。要求在用户输人学生序号以后,能输出该学生的全部成绩。用指针函数来实现。

#include <stdio.h>

// 指针数组
int main(){
	int a = 101;
	int b = 102;
	int c = 103;
	int d = 104;
	
	
	 //指针数组
	//  p[4] = {&a,&b,&c,&d} 表示这四个变量都是一个地址  , * p[4] 对这四个地址取值
	int * p[4] = {&a,&b,&c,&d};  //强调的是一个数组
	for(int i  = 0 ;i <4;i++){
		printf("值是%d\n", *(p[i]));
	}
	return 0;

用函数调用指针数组,打印所有的值

#include <stdio.h>
#include <stdlib.h>
//指针数组
int  a = 20;
int b = 45;
 
int inarr(int a ,int b){
	
	return  a > b ?a :b ;
}

int petarr(int a ,int b){
	 
	return  a < b ?a :b ;
}
 
 int getarr(int a ,int b){
	
	return a + b ;
 }

 
 int main(){
	 
	 int data ;
	 //  (*p)(int a ,int b ); 函数指针
	 
	  // 定义 函数指针数组,包含三个指向函数的指针
	 int (* pfunc[3])(int a ,int b ) = { 
	 // pfunc[3] = {inarr,petarr,getarr}表示这四个函数都是一个地址 ,* pfunc[3] 对这3个函数取值
		 inarr,
		 petarr,
		 getarr}; //函数 指针数组
	 
	 for(int i = 0; i<3;i++){
		 data =(* pfunc[i])(a,b);  // 调用函数指针数组
		 printf("data = %d\n",data);
	 }
	 return 0;
	 
 }

指针函数: int * p(int x,inty);

概念:

注意:指针函数

int * p(int a,int b)是一个函数

它的返回值的类型是一个指针类型;函数指针

int *p(int a,int b)

是一个指针,它是定义一个指针变量指向一个函数。

编程练习:

例8.25有a个学生,每个学生有b门课程的成绩。要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。

#include <stdio.h> 
                    
int* GetADDFrom(int position,int (*pa)[4]){
	int *p2;
	p2 = (int *)(pa+position);
	return p2;  // 返回一个指针地址
}

int main()
    
{
	int arry[3][4] = {{45,65,94,25},{81,56,63,58},{58,59,57,56}};
	int position;
	
	printf("请输入学生位号\n");
	scanf("%d",&position);
	int *p;
	p = GetADDFrom(position,arry);  //调用函数
	
	for(int i=0;i<4;i++)
	{
		printf("%d ",*p++);
	}

    return 0;
}

例8.26对例8.25中的学生,找出其中有不及格的课程的学生及其学生号。

#include <stdio.h>
  // GetAddFrom函数:根据给定的position和二维数组指针p,二级指针p2指向数组中对应行的起始地址
int *GetAddFrom(int position,int (*p)[4])
{
	// *p2 = (int *)(p+position);//将父数组的首地址 赋值给 指针 **P2的地址。
	int *p2;
	p2 = (int *)(p+position);
	return p2;
}
int main()
{
	int a[3][4] = {{45,65,94,25},{81,56,63,58},{58,59,57,56}};
	
	int position;
	int *p;
	
	printf("请输入学生号数\n");
	scanf("%d",&position);
	
	p = GetAddFrom(position,a);
	
	for(int i=0;i<4;i++)
	{
		printf("%d \n",*p++);
	}
	printf("doen!\n");
	
	
	for(int i=0;i<3;i++)
	{
		for(int j = 0;j<4;j++){
			
			if(a[i][j]<60)
			{
				printf("数据是:%d,第%d行,第%d列, \n",*(a[i]+j),i,j);
							
			}
		
	}
	}
	return 0;
}

二级(多级)指针(套娃):

如何定义一个二级指针: int **p

编程练习

#include <stdio.h> //引入标准输入输出库
// 二级指针
int main() //主函数
{
    int data = 10; //定义一个整型变量 data 并初始化为 10

    int *p = &data; //定义一个指针 p 并将其赋值为 data 的地址

    printf("data 的地址是%p\n", &data); //打印 data 的地址
    printf("P保存 data 的地址是%p,data 的内容是%d\n", p, *p); //打印指针 p 中保存的地址和其所指向的变量的值

    printf("p自己的地址是:%p\n", &p); //打印指针 p 自己的地址

    /*int *pp; //定义一个指针 pp
    pp = &p; //将 pp 的值设置为指针 p 的地址

    printf("pp保存p的地址是%p,\n", pp); //打印指针 pp 中保存的值,即指针 p 的地址
    printf("*pp保存的地址是%p\n", *pp); //打印指针 pp 中所指向的变量的值,即指针 p 中保存保存 data 的地址
*/
    int **p2; //定义一个二级指针 p2
    p2 = &p; //将二级指针 p2 的值设置为指针 p 的地址

    printf("p2保存的p 地址%p\n", p2); //打印二级指针 p2 中保存的值,即指针 p 的地址
    printf("*p2是对p地址中的地址进行打印%p\n", *p2); //打印二级指针 p2 中所指向的变量的值,即指针 p 中保存 data 的地址

    printf("**p2是对p地址中的地址中的值进行打印%d\n", **p2); //打印二级指针 p2 中所指向的变量的值,即 data 变量的值

    return 0; //程序结束并返回 0
}

编程练习:对例8.25的程序修改,不使用函数指针

#include <stdio.h>
 // GetAddFrom函数:根据给定的position和二维数组指针p,二级指针p2指向数组中对应行的起始地址
void GetAddFrom(int position,int (*p)[4],int **p2)
{
	// *p2 = (int *)(p+position);//将父数组的首地址 赋值给 指针 **P2的地址。
	
	*p2 = (int *)(p+position);         
}
int main()
{
	int a[3][4] = {{45,65,94,25},{81,56,63,58},{58,59,57,56}};
	
	int position;
	int *p;
	
	printf("请输入学生号数\n");
	scanf("%d",&position);
	
	// 调用GetAddFrom函数,传入位置号和数组a的地址,并将得到的地址存储到p指针中
	GetAddFrom(position,a,&p); // 传入指针 &p 自己的地址
	
	for(int i=0;i<4;i++)
	{
		printf("%d \n",*p++);
	}

	printf("doen!\n");
	
	for(int i=0;i<3;i++)
	{
		for(int j = 0;j<4;j++){
			
			if(a[i][j]<60)
			{
				printf("数据是:%d ,第%d行,第%d列,\n",*(a[i]+j),i,j);
				
			}
		
		}
	}

	return 0;
}

注意:二级指针并不等同于二维数组!

#include <stdio.h>
 
int main()
{
	int a[3][4] = {{45,65,94,25},{81,56,63,58},{58,59,57,56}};
	
	int (*p)[4] = a;
// 	int **p = a;
	
	printf("p = %p\n",p);
	printf("a = %p\n",a);
	printf("*a = %p\n",*a);  //打印的是列地址,
	printf("%p\n",*p);		//*p是野指针,不是我们认为的列地址
	
	**p = 100;
	printf("%d ",a[0][0]);
	
	/*
		因为二级指针并不等同于二维数组,*p是一个野指针,所以当用**p访问二维数组时并不能改变里面的值,
		它执行的结果是一个段错误
	*/
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值