C语言指针

C语言指针,第1张

目录

指针引入:

开胃小菜:

指针类型:

指针作用:

 通过指针指向数组:

指针和二维数组:

 数组指针:

 函数指针:

指针数组:

 指针函数:

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

 二级指针:

 总结:


 

指针引入: 开胃小菜:

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

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

#include 

int main()
{
	//什么是整型变量?存放整型数的变量
	//什么是字符型变量?存放字符型数据的变量
	//什么是指针型变量?存放指针的变量
	
	int a = 10;
	int *p = &a;								//这里的*是标识符,用来定义指针变量
	
	printf("打印a的地址:%p\n",&a);
	printf("变量名访问输出:a = %d\n",a);
	printf("地址访问输出:a = %d\n",*(&a));		//这里的*是取值运算符,把后面地址内的数据取出来
	printf("指针变量访问输出:a = %d\n",*p);	//这里的*是取值运算符,把后面地址内的数据取出来(作用同上)
	
	return 0;
}

 由此可见,指针 = 地址

指针变量和其他类型的变量一样,也有四要素:变量名、类型、(存储空间)地址、值

有关指针变量注意事项

① 定义指针变量要区分类型(详情见下文);

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

取地址符号&

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

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

指针类型:

代码验证:

#include 

int main()
{
	int a = 0x4567;
	int *p = &a;
	char *c = &a;
	
	printf("a的地址是:%p\n",p);
	printf("a的地址是:%p\n",c);
	
	printf("a的值是:a = %x\n",*p);
	printf("a的值是:a = %x\n",*c);
	
	printf("a的地址加1是:%p\n",p+1);
	printf("a的地址加1是:%p\n",c+1);
	
	return 0;
}

       因为定义是一个char型变量,所以在输出int型变量时高位被削掉了,只能输出1字节(8位)的数据,而且在进行地址加1时,int型变量加的是4位,而char型变量加的是1位。所以在定义指针变量时要区分类型。 

      由此可见:定义哪种类型的指针变量并不会影响变量的地址,但取值的时候出了问题,取值运算符会根据指针变量类型,访问不同大小的存储空间。区分指针变量类型不仅能够决定指向空间的大小,而且还能决定增量。

指针作用:

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

/*
	从前的交换方式
#include 

int main()
{
	int data1 = 10;
	int data2 = 20;
	int temp;
	
	puts("原样输出");
	printf("data1 = %d data2 = %d\n",data1,data2);
	
	temp = data1;
	data1 = data2;
	data2 = temp;
	
	puts("交换后输出");
	printf("data1 = %d data2 = %d\n",data1,data2);
	
	return 0;
}
*/


/*
	交换失败!!!原因:main函数将参数的值copy到ChangeData函数中,在ChangeData函数中,
	发生交换的是该函数的data1,data2,与main函数中的data1,data2没有任何关系,因为两函数
	中的地址不同,所以main函数中的data1,data2的值始终不会发生改变,自然交换失败。
*/
/*
	将函数封装交换data1和data2的值
#include 

void ChangeData(int data1,int data2)
{
	int temp;
	temp = data1;
	data1 = data2;
	data2 = temp;
}

int main()
{
	int data1 = 10;
	int data2 = 20;
	
	puts("原样输出");
	printf("data1 = %d data2 = %d\n",data1,data2);
	
	ChangeData(data1,data2);
	
	puts("排序后输出");
	printf("data1 = %d data2 = %d\n",data1,data2);
	
	return 0;
}
*/
//封装函数data1和data2交换,交换成功
#include 

void ChangeData(int *data1,int *data2)
{
	int temp;
	temp = *data1;
	*data1 = *data2;
	*data2 = temp;
}

int main()
{
	int data1 = 10;
	int data2 = 20;
	
	puts("原样输出");
	printf("data1 = %d data2 = %d\n",data1,data2);
	
	ChangeData(&data1,&data2);
	
	puts("排序后输出");
	printf("data1 = %d data2 = %d\n",data1,data2);
	
	return 0;
}

编程练习:使用指针指向一个固定的地址

#include 

int main()
{
	int a;
	printf("adderss of a is : 0x%p\n",&a);	//获得一段可 *** 作的地址空间
	int *p = (int *)0x000000000061FE1D;
	printf("address of p is : 0x%p\n",p);
	
	return 0;
}

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

#include 

void InitData(int *a,int *b,int *c )
{
	puts("请输入三个整数");
	scanf("%d%d%d",a,b,c);
	puts("输入完毕");
}

void CompareData(int *a,int *b,int *c)
{
	int temp;
	if(*a<*b)
	{
		temp = *a;
		*a = *b;
		*b = temp;
	}
	if(*a<*c)
	{
		temp = *a;
		*a = *c;
		*c = temp;
	}
	if(*b<*c)
	{
		temp = *b;
		*b = *c;
		*c =temp;
	}
	
}

int main()
{
	int a;
	int b;
	int c;
	
	InitData(&a,&b,&c);
	
	printf("原样输出\n");
	printf("a = %d,b = %d,c = %d\n",a,b,c);
	
	CompareData(&a,&b,&c);
	printf("排序后输出\n");
	printf("a = %d,b = %d,c = %d\n",a,b,c);
	
	return 0;
}

 通过指针指向数组:

 编程练习:

#include 

int main()
{
	int a[3] = {1,2,3};
	int *p;
	p = &a[0];
	p = a;
	printf("address of a[0] is %p,a[0] = %d\n",&a[0],*(&a[0]));
	printf("address of a is %p,a = %d\n",&a,*a);
	
	printf("address of a[0] is %p,a[0] = %d\n",p,*p);	//数组的首地址就是首个元素的地址
	printf("address of a is %p,a = %d\n",p,*p);			//数组名就是数组的首地址
	
	return 0;
}

指针偏移遍历数组:

#include 

int main()
{
	int a[3] = {1,2,3};
	int *p;
	p = a;
	
	for(int i = 0;i<3;i++)
	{
		printf("%d ",a[i]);
		printf("%d ",*(a+i));
	}
	
	return 0;
	
}	

注意: 

#include 

int main()
{
	int a[3] = {1,2,3};
	int *p = a;
	
	for(int i = 0;i<3;i++)
	{
		printf("%d ",*p++);
	}
	p = a;			//数组初始化!!!当使用指针偏移来访问数组时,要注意指针是否越界
	for(int i = 0;i<3;i++)
	{
		printf("%d ",*p++);
	}
	
	return 0;
}

见怪不怪的表达方法:

 注意:当把数组名拿来++;sizeof地址大小不一样

#include 

int main()
{
	int a[3] = {1,2,3};
	int *p = a;
	
	printf("sizeof a is: %d\n",sizeof(a));			//代表数组a的大小,3*4个字节大小
	printf("sizeof p is: %d\n",sizeof(p));			//代表OS *** 作系统存放一个地址所用的字节大小
	printf("sizeof int* is: %d\n",sizeof(int *));	//代表OS *** 作系统存放一个地址所用的字节大小
	printf("sizeof char* is: %d\n",sizeof(char *));	//代表OS *** 作系统存放一个地址所用的字节大小

	for(int i = 0;i<3;i++)
	{
		printf("%d ",p[i]);
	}
	putchar('\n');
	for(int i = 0;i<3;i++)
	{
		printf("%d ",a[i]);
	}
	putchar('\n');
	for(int i = 0;i<3;i++)
	{
		printf("%d ",*p++);
	}
	/*for(int i = 0;i<3;i++)
	{
		printf("%d ",*a++);	//a作为数组名代表a这个数组的地址空间,一旦确定就不能改变,所以不能拿来++
	}*/
	putchar('\n');
	
	return 0;
}

 练习函数指针结合:

#include 

void InitArray(int *p,int len)
{
	int i;
	for(i=0;i

 练习数组指针逆序输出:

#include 

void InitArray(int *p,int len)
{
	int i;
	for(i=0;i

指针和二维数组:

将二维数组看成父子数组,其中a是父数组的数组名(面向行的地址),a[0],a[1],a[2]等是子数组的数组名(面向列的地址)。

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

 数组名就是数组的首地址,因此数组的首地址就是首个元素的地址,即:a[0] = &a[0][0];

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

答:a是父数组的地址(即面向二维数组的行地址);

a[0]是子数组的地址(即面向二维数组的列地址);

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

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

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

编程验证:

#include 

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);
	
	return 0;
}

 树立认知:

提示:

进阶:

 有意思的思考:

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

小总结:

 代码验证:

#include 

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<3;i++)
	{
		for(j=0;j<4;j++)
		{
			printf("address: %p,value: %d\n",&a[i][j],a[i][j]);
			printf("address: %p,value: %d\n",a[i]+j,*(a[i]+j));
			printf("address: %p,value: %d\n",*(a+i)+j,*(*(a+i)+j));
			printf("=======================================\n");
		}
	}
	
	return 0;
}

 数组指针:

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

#include 

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<3;i++)
	{
		for(j=0;j<4;j++)
		{
			printf("%d ",a[i][j]);
		}
	}
	*/
	/*
	失去二维数组的意思(差点感觉)
	int *p = &a[0][0];
	
	for(i=0;i<3;i++)
	{
		for(j=0;j<4;j++)
		{
			printf("%d ",*p++);
		}
	}
	*/
	int (*p2)[4];		//定义一个数组指针,指向数组!
	p2 = a;				//数组指针才真正等同于二维数组名
	
	for(i=0;i<3;i++)
	{
		for(j=0;j<4;j++)
		{
			printf("%d ",*(a[i]+j));
			//printf("%d ",*(*(a+i)+j));   作用同上
		}
	}
	
	return 0;
}

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

#include 

void Inputarray(int *line,int *list)
{
	printf("请输入行列值\n");
	scanf("%d%d",line,list);
}

int Printfarray(int (*p)[4],int line,int list)
{
	int data;
	data = *(*(p+line)+list);
	return data;
}

int main()
{
	int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,100,12,18}};
	int line;
	int list;
	int result;
	int (*p)[4];
	p = a;
	
	Inputarray(&line,&list);
	
	result = Printfarray(a,line,list);
	
	printf("%d ",result);
	return 0;
}

 函数指针:

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

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

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

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

例如:

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

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

代码验证:

#include 

void PrintWelcome()
{
	printf("Welcome come to C World\n");
	printf("=============================\n");
}

int ChangeData(int data)
{
	return data * 2;
}

int main()
{
	int data = 20;
	int result;
	PrintWelcome();
	void (*p)();
	int (*p2)(int data);
	
	p = PrintWelcome;
	p2 = ChangeData;
	
	(*p)();
	result = (*p2)(data);
	printf("result = %d\n",result);
	
	return 0;
}

 编程练习:

#include 
#include 

int GetMaxData(int data1,int data2)
{
	return data1>data2?data1:data2;
}

int GetMinData(int data1,int data2)
{
	return data1

指针数组:

定义:

 注意:指针数组(int * p[4])是一个数组,数组内存放的每一项都是一个指针变量;数组指针(int(*p)[4])是一个指针,它是定义一个指针变量指向一个数组。

编程小练习:遍历指针数组

#include 

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	
	int *p[4] = {&a,&b,&c,&d};		//定义一个指针数组存放指针变量
	int i;
	
	for(i=0;i<4;i++)
	{
		printf("%d ",*p[i]);
	}
	
	return 0;
}

 编程练习:定义一个函数指针数组:

#include 
#include 

int GetMaxData(int data1,int data2)
{
	return data1>data2?data1:data2;
}

int GetMinData(int data1,int data2)
{
	return data1

 指针函数:

定义:

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

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

编程练习:

#include 

int *GetADDFrom(int position,int (*p)[4])
{
	int *p2;
	p2 = (int *)(position+p);
	return p2;
}

int main()
{
	int a[3][4] = {{57,64,68,56},{80,87,60,45},{78,65,30,45}};
	int position;
	int *p;
	
	printf("请输入学生位号\n");
	scanf("%d",&position);
	
	p = GetADDFrom(position,a);
	
	for(int i=0;i<4;i++)
	{
		printf("%d ",*p++);
	}
	
	
	return 0;
}

 编程练习:

#include 

int *GetAddFrom(int position,int (*p)[4])
{
	int *p2;
	p2 = (int *)(position+p);
	return p2;
}

int main()
{
	int a[3][4] = {{56,85,57,80},{58,90,78,69},{45,50,48,59}};
	int position;
	int *p;
	
	printf("请输入学生号\n");
	scanf("%d",&position);
	
	p = GetAddFrom(position,a);
	for(int i=0;i<4;i++)
	{
		printf("%d ",*p++);
	}
	putchar('\n');
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<4;j++)
		{
			if (a[i][j]<60)
			{
				printf("In the %d column,in the %d row is %d\n",i+1,j+1,*(a[i]+j));
			}
		}
	}
	
	return 0;
}

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

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

编程验证:

#include 

int main()
{
	int a = 10;
	int *p = &a;
	
	printf("a的地址是%p\n",&a);
	printf("p存放的是a的地址:%p,通过p访问a的值是 :%d\n",p,*p);
	printf("p的地址是%p\n",&p);
	
	/*int *p2 = &p;
	printf("p2存放的是p的地址:%p\n",p2);	//使用一级指针指向一级指针时,访问不到a地址中保存的数据!!!
	printf("*p2访问的是:%p\n",*p2);
	*/
	int **p3 = &p;
	printf("p3存放的是p的地址:%p\n",p3);
	printf("*p3存放的是a的地址:%p\n",*p3);
	printf("**p3存放的是a的值:%d\n",**p3);
	
	int ***p4 = &p3;
	printf("p3的地址是%p\n",&p3);
	printf("p4存放的是p3的地址:%p\n",p4);
	printf("*p4存放的是p的地址:%p\n",*p4);
	printf("**p4存放的是a的地址:%p\n",**p4);
	printf("***p4存放的是a的值:%d\n",***p4);
	
	
	return 0;
}

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

#include 

int *GetAddFrom(int position,int (*p)[4],int **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(position,a,&p);
	
	for(int i=0;i<4;i++)
	{
		printf("%d ",*p++);
	}
	
	return 0;
}

 二级指针:

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

编程验证:

#include 

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;
}

 正确表达:

#include 

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访问二维数组时并不能改变里边的值,
		它执行的结果是一个段错误
	*/
	
	int **p2 = &p;
	**p2 = 100;
	printf("%d\n",a[0][0]);
	
	return 0;
}

 总结:

 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/2990404.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)