指针学习

一、什么是地址

地址就是程序中的一个内存,程序中的地址一般以十六进制的方式进行显示。

在程序中写一个数据,或者在程序中创建一个变量,程序都会为其分配一块内存,这样他们就拥有了地址。

变量的地址:

1.变量一旦设置出来,那么他的地址就固定了,这个地址就是一个常量,无论在这个变量中储存什么数据,这个地址都不会改变。

2.本质上来讲,变量中所储存的数据,其实是储存在变量所在的地址上面。

3.我们在之前讨论过,不同函数中,相同名称的变量也不是一个变量,根本原因就在于地址不一样,在主函数中创建的变量names,程序会分配给他一块内存,他就有了相应的地址,在其他函数中同样创建变量names,程序也会分配给这个names变量一个内存,他也有了相应的地址,由于这两个地址不可能相同,因此这两个变量也就不一样。

通过一个程序来研究:

//研究变量的地址
#include	<stdio.h>

int	main(void)
{
	int	pooh=0;
	int n;
	for (n=0;n<10;n++)
	{
		pooh+=2;//每次循环都改变变量pooh中储存的数据。
		printf("打印pooh的值:%d,打印pooh的地址:%p\n",pooh,&pooh);
	}
	return	0;
 } 

打印pooh的值:2,打印pooh的地址:000000000062FE18
打印pooh的值:4,打印pooh的地址:000000000062FE18
打印pooh的值:6,打印pooh的地址:000000000062FE18
打印pooh的值:8,打印pooh的地址:000000000062FE18
打印pooh的值:10,打印pooh的地址:000000000062FE18
打印pooh的值:12,打印pooh的地址:000000000062FE18
打印pooh的值:14,打印pooh的地址:000000000062FE18
打印pooh的值:16,打印pooh的地址:000000000062FE18
打印pooh的值:18,打印pooh的地址:000000000062FE18
打印pooh的值:20,打印pooh的地址:000000000062FE18

根据程序的运行结果可以发现,只要将变量设置出来,无论变量储存的数据是什么,地址都不会改变。

二、取地址运算符&

在变量前面加上取地址运算符,这个整体表示的就是一个地址,这个地址是这个变量的地址。

通过转换说明%p能够打印出这个地址。

//通过转换说明%p来打印地址
#include	<stdio.h>

int	main(void)
{
	int paper=3;
	double max=2.44;
	int names=345;
	long length=98777;
	
	printf("print the adress of paper :%p\n",&paper);
	printf("print the adress of max :%p\n",&max);
	printf("print the adress of names :%p\n",&names);
	printf("print the adress of length :%p\n",&length);
	
	return	0;	
} 

四、解引用运算符*

在地址前面加上解引用运算符,得到的就是这个地址上面所储存的数据。

*&names=names中所储存的数据,可以这样理解, *和&遇到就会互相抵消,剩下的事变量中的数据。

//解引用运算符的使用
#include	<stdio.h>

int	main(void)
{
	int vapper=9;
	
	printf("print the adress of vapper :%p\n",&vapper);
	printf("open the adress of vapper :%d\n",*&vapper);
	
	int names;
	printf("print the adress of names :%p\n",&names);
	printf("open the adress of names :%d\n",*&names);
	
	return	0;
	
 } 

运行结果:

print the adress of vapper :000000000062FE1C
open the adress of vapper :9
print the adress of names :000000000062FE18
open the adress of names :1

通过这个运行结果我们可以发现:如果地址上面没有储存数据,那么对这个地址进行解引用运算的话,会自动生成一个值,这个值不固定。

五、引出指针

什么是指针:指针是一个储存地址的变量。

指针的赋值:指针是一个储存地址的变量,因此可以把对应类型的地址、地址表达式、指针、指针表达式赋值给指针。

1)基础指针:指向数值的指针

例如:int *pt表示的是 指向int型 的一个指针,也就是说pt储存的是int形变量的地址,这个地址上面储存的是一个int型数据。

声明指向数值的指针:

int * ptint:指向int型的一个指针ptint;里面储存的是一个int型变量的地址

double *ptdouble:指向double型的一个指针ptdouble;里面储存的是一个double型变量的地址

char *ptchar:指向char型的一个指针ptchar;里面储存的是一个char型变量的地址

简单了解指针:

//简单的了解以下指针
#include	<stdio.h>

int	main(void)
{
	int pooh=3;
	int *ptint=&pooh;//设置一个能够指向int型的指针,并且把一个int型变量的地址赋值给这个指针。
	printf("pooh在程序中的地址是:%p,pooh所储存的值是:%d\n",&pooh,pooh);
	printf("ptint储存的地址是:%p,ptint所指向的值是:%d\n",ptint,*ptint);
	
	return	0;
	
 } 
1.指针的解引用

由于指针储存的是地址,因此对指针进行解引用相当于对地址进行解引用,得到的结果就是地址上面储存的值。

如果一个指针没有进行初始化,(这个指针中没储存任何地址),都不能对这个指针进行解引用操作,这样会使程序发生崩溃。

指针可以初始化为NULL,NULL声明在stdio.h中,这是个空地址,初始化为空地址的指针叫做空指针,这样做可以避免因错误的解引用没有地址的指针而造成程序崩溃。

2)一维数组

数组能够和指针联系起来:

数组是一系列数据类型相同的数据的组合,每个数据都储存在相应的元素中,因为元素是变量,变量是有地址的,所以能够和指针联系起来。

在一维数组中,我们使用基础指针。

*重点:数组名称就是一个指针,并且这个指针不需要再次声明,这个指针中储存的地址是:数组中第一个元素的地址。

下面我们来讨论指针的一些运算法则(这些运算法则应用到数组中才有实际意义)

1.在一维数组中指针的偏移

地址的偏移:

我们先来了解一下一维数组的创建过程:

我们创建一个一维数组,必须要做的就是确定这个数组的大小(元素的个数),系统会根据数组的大小分配相应的内存,这些内存又被这些元素平均分配,每个元素都有一块内存,也就是每个元素都对应着一个地址,这些地址是连续的,并且之间相隔的距离是一个储存单元。(储存单元,int型变量的内存是4字节,储存单元就是4字节,double型变量的内存是8字节,储存单元就是8字节)

地址表达式:地址+或-一个整数,加上一个整数表示在此基础上正向偏移多少个储存单元,减去一个整数表示在此基础上反向偏移多少个储存单元。(进行正向偏移或者反向偏移不能够超出数组本身的大小)

指针的偏移就是地址的偏移。

//地址的偏移以及指针的偏移
#include	<stdio.h>

int	main(void)
{
	int dates[6]={1,2,3,4,5,6};
	
	printf("print the fifth number's adress:%p\n",&dates[4]);
	printf("print the fifth number's adress:%p\n",&dates[3]+1);
	printf("print the fifth number's adress:%p\n",dates+4);//dates是数组名,因此是一个指针 
	printf("print the fifth number's adress:%p\n",&dates[5]-1);
	
	return	0; 
	
 }  

打印结果如下:

print the fifth number's adress:000000000062FE10
print the fifth number's adress:000000000062FE10
print the fifth number's adress:000000000062FE10
print the fifth number's adress:000000000062FE10

地址加减整数得到一个新地址,这个新地址是这个地址表达式的结果,并不会更改参与运算的地址的值。

通过上面程序可以发现,同一个数组中的不同的地址通过偏移,都能表示出同一个地址。

2.在一维数组中指针的复合运算

*重点:数组名称这个指针只能代表首元素的地址,不能进行递增递减操作。

正常情况下:一个指针会固定的储存一个变量的地址,但可以通过指针的复合运算来更改指针中储存的地址。

//一维数组中指针的复合运算
#include	<stdio.h>

int	main(void)
{
	int dates[9]={1,2,3,4,5,6,7,8,9};
	
	int	*ptint=&dates[5];//指针储存的是数组中第六个元素的地址。 
	
	ptint++;
	printf("%p\n",&dates[6]);
	printf("print the adress of ptint:%p\n",ptint);//现在指针储存的是第七个元素的地址。
	printf("open the ptint :%d\n",*ptint); 
	
	ptint-=3;
	printf("%p\n",&dates[3]);
	printf("print the adress of ptint:%p\n",ptint);//现在指针储存的是第四个元素的地址。
	printf("open the ptint :%d\n",*ptint); 
	
	return	0; 
 } 

该程序的运行结果如下:

000000000062FE08
print the adress of ptint:000000000062FE08
open the ptint :7
000000000062FDFC
print the adress of ptint:000000000062FDFC
open the ptint :4

通过指针的复合运算,改变了指针中储存的地址,由于地址不同,那么解引用出来的结果也不同。

3.在一维数组中指针的求差

在同一个数组中,不同元素的地址可以通过加减储存单元来相互得到,因此对两个地址或者两个指针求差能够得到二者之间相差的储存单元。

//对一维数组中的地址或者指针求差
#include	<stdio.h>

int	main(void)
{
	int	dates[8]={1,2,3,4,5,6,7,8};
	int	*ptint=&dates[3];
	
	printf("print the two points'distance:%d\n",ptint-dates);//dates是一个指针,储存的是首元素的地址。
	printf("print the two points'distance:%d\n",dates-ptint); 
	
	return	0;
 } 

程序的运行结果如下:

print the two points'distance:3
print the two points'distance:-3

通过这个程序可以发现,地址之间的距离是有正负的。

4.接受一维数组的指针形参

接受一维数组的指针形参,例如:int *point 这就可以作为一个接受一维数组的指针形参,这个可以接受指向int型的指针或者int型变量的地址。

指针形参使用的好处在于,指针中储存的是地址,能够直接访问原始数据,并且对原始数据做出更改。

//接受一维数组的指针形参 
#include	<stdio.h>
#define SIZE 10
long sum(int *point,int n);

int	main(void)
{
	int dates[SIZE]={20,10,5,39,4,16,19,26,31,20};
	long answer;
	
	answer=sum(dates,SIZE);//定义一个函数,这个函数能够对数组进行求和
	printf("The total number of dates is %ld\n",answer);
	
	return	0;	
 }

long sum(int *point,int n)//指针作为函数定义中的形式参数 
{ //这个sum函数接受两个参数,一个是指向整形的指针,一个是整形n,能够传递过来的要么是指向整形的指针,要么是整型变量的地址 
	int index;
	long total=0;
	for (index=0;index<n;index++)
	{
		total+=*(point+index);//对指针进行解引用 
		//这里也能写成point[index]这是指针特有的一种写法
	}
	return	total;
 }

在c语言中,也可以用数组形式来表示接受一位数组的指针形参,本质上都是指针,只是为了便于理解,用数组形式表示。

//利用数组形式表示一个接受一位数组的指针形参
#include	<stdio.h>
#define SIZE 10
long sum(int array[],int n);

int	main(void)
{
	int dates[SIZE]={20,10,5,39,4,16,19,26,31,20};
	long answer;
	
	answer=sum(dates,SIZE);//定义一个函数,这个函数能够对数组进行求和(这个dates就是指向int型的指针) 
	printf("The total number of dates is %ld\n",answer);
	
	return	0;	
 }

long sum(int array[],int n)//数组形式作为函数定义中的形式参数,本质上是一个指针//只有方括号是空的才能够表示本质是指针。
//并且这个指针接受一个指向int型的指针或者一个int型变量的地址。 
{
	int index;
	long total=0;
	for (index=0;index<n;index++)
	{
		total+=array[index];//这里同样是使用数组形式 
	}
	return	total;
 }
5.const在基础指针中的应用

const有四种形式能够对数组中的数据做出保护。

如果数组中的数据一直都不会更改,那么可以直接将数组声明为常数组。

const int name[20];

如果数组中的数据在之后会进行改变,但是不希望我所使用的指针不小心对数据做出改变,可以在声明指针的时候使用const

写法:const int *point;

这种写法是能改变指针中储存的地址,但是不能改变其所储存地址上的值。

如果出现对数组中的数据做出更改的操作时,系统会报错。

//对指针使用const避免对数据做出更改
#include	<stdio.h>
#define SIZE 10
int	main(void)
{
	int	names[SIZE]={1,2,3,4,5,6,7,8,9,0};
	
	const int *ptint=names;//这样声明的指针,可以改变这个指针所表示的地址,但是不能改变所指向地址的值。 
	
	printf("打印ptint所指向的数值以及地址:\n%d\n%p\n",*ptint,ptint);//这个指针现在的地址是始首元素的地址 
	
	ptint++;//这步操作时改变了ptint指针的地址,既然地址改变了,那么他指向的值也就一定会改变的。 
	printf("打印ptint所指向的数值以及地址:\n%d\n%p\n",*ptint,ptint);
	
	int *ptint_1=&names[2];//再次定义一个指针,这个指针现在的地址是第三个元素的地址 
	//由于没有使用const在前面声明,因此我可以改变第三个元素所储存的值
	printf("打印ptint_1所指向的数值以及地址:\n%d\n%p\n",*ptint_1,ptint_1);
	*ptint_1=99;
	printf("打印ptint_1所指向的数值以及地址:\n%d\n%p\n",*ptint_1,ptint_1);//可以发现,地址没变但是地址上面的值变了
	/*const int *ptint_2=names[4];
	*ptint_2=66; 
	printf("%d",*ptint_2); */ //注释掉的部分在程序中是运行不了的,因为指针是const型的,不能更改值,所以会报错。 
	
	return	0; 
 } 

运行结果:

打印ptint所指向的数值以及地址:
1
000000000062FDE0
打印ptint所指向的数值以及地址:
2
000000000062FDE4
打印ptint_1所指向的数值以及地址:
3
000000000062FDE8
打印ptint_1所指向的数值以及地址:
99
000000000062FDE8

如果我不想更改指针中所储存的地址,但是我想要更改这个地址上面所储存的数据,声明方式如下:

int *const point;

//使用const来确保指针中的地址不会更改
#include	<stdio.h>

int	main(void)
{
	int	dates[8]={1,2,3,4,5,6,7,8};
	
	int	*const ptint=dates;
	
	printf("打印这个地址以及这个地址所储存的值:\n");
	printf("%p\n%d\n",ptint,*ptint);
	
	/*ptint++;
	printf("打印这个地址以及这个地址所储存的值:\n");
	printf("%p\n%d\n",ptint,*ptint);*/       //注释中的这部分,是对这个ptint的地址进行修改,由于这种声明方式是不能改变地址的,因此不正确。 
	
	*ptint=99;
	printf("打印这个地址以及这个地址所储存的值:\n");
	printf("%p\n%d\n",ptint,*ptint);
	
	return	0; 
}

运行结果如下:

打印这个地址以及这个地址所储存的值:
000000000062FDF0
1
打印这个地址以及这个地址所储存的值:
000000000062FDF0
99

如果我既不想更改这个指针所储存的地址,又不想更改这个地址上的值,那么我可以采用双const来声明这个指针。

const int *const point;

//使用双const来声明一个指针
#include	<stdio.h>

int	main(void)
{
	int	dates[8]={1,2,3,4,5,6,7,8};
	
	const int *const ptint=dates;
	
	printf("打印这个指针的地址和储存的值:\n");
	printf("%p\n%d\n",ptint,*ptint);
	
	/**ptint=4;
	ptint++; */  //注释掉的这部分包括修改这个指针的地址和地址上面储存的值,是不正确的,因此会使系统报错 
	
	return	0;
 } 
6.const在接受一维数组的指针形参中的应用

1.如果我希望定义函数中的指针不会改变地址上面所储存的数据,那么形式参数设置成:

const int *point;

2.如果我们希望定义函数中的指针不会改变自身的地址,那么形式参数设置成:

int *const point;

3.如果我们希望上面两点同时成立,那么形式参数设置成:

const int *const point;

3)二维数组

我们来深刻的学习一下二维数组的知识。

首先我们要明确,一维数组是由众多元素组成的,而二维数组是由众多一维数组组成的。

例如:int dates[20] [4]这个数组是一个二维数组,这个数组由20个一维数组组成,并且每个一维数组由四个元素组成。

1.二维数组中的知识点

声明一个二维数组:int dates[4] [3];

这个二维数组中包含着四个一维数组:dates[0] [3]、dates[1] [3]、dates[2] [3]、dates[3] [3]

dates[0]是第一个一维数组的名称,同时也是二维数组的首元素,&dates[0]是第一个数组的地址。

dates[1]是第二个一维数组的名称,同时也是二维数组的第二个元素,&dates[1]是第二个数组的地址。

dates[2]是第三个一维数组的名称,同时也是二维数组的第三个元素,&dates[2]是第三个数组的地址。

dates[3]是第四个一维数组的名称,同时也是二维数组的第四个元素,&dates[3]是第四个数组的地址。

一)首先:我们说过数组的名称是一个指针,这个指针储存的是数组中第一个元素的地址。

因此:二维数组的名称储存的就是其首元素的地址,而对于二维数组而言,他的首元素是一维数组。因此储存的就是一维数组的地址。

dates是一个二维数组的名称,也是一个指针,这个指针储存的是一维数组dates[0] [3]的地址。

*我们把这个指针叫做指向数组的指针,也就是我们的进阶指针。

4)进阶指针:指向数组的指针

例如:int (*ptint)[3]表示一个指向数组的指针,ptint指向的是拥有三个元素的一维数组,储存的是这个一维数组的地址。

1.在二维数组中指针的偏移

*重点:数组的地址和数组首元素的地址是相同的,二者共用一个地址,但是又不完全相同,可以通过地址的偏移来解释。

//数组的地址和数组首元素的地址的偏移
#include	<stdio.h>

int	main(void)
{
	int names[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};//names是一个指向数组的指针,储存的是第一个一维数组的地址。
	
	//names[0]是第一个一维数组的名称,是一个指向数值的指针,储存的是这个数组中第一个元素的地址
	//我们说过,数组的地址和数组的首元素的地址是一个,names中储存的就是第一个数组的地址,names[0]中储存的就是第一个数组的首元素的地址
	//所以这两个地址应该是相同的
	printf("print the adress of the point names :%p\n",names);
	printf("print the adress of the point names[0]:%p\n",names[0]);
	//上述的打印结果完全相同,我们来研究这两个指针的不同之处
	//由于names是指向数组的指针,对这个指针进行偏移,指针表达式表示的应该是不同数组的地址
	//names[0]是指向数值的指针,对这个指针进行偏移,指针表达式表示的应该是不同元素的地址 
	printf("print the adress of the expression :%p\n",names+1);
	//names+1表示的是第二个一维数组的地址,每一个一维数组中有三个元素,每一个元素的地址之间相差一个储存单元,所以
	//数组之间的地址相差3个储存单元 
	printf("print the adress of the expression :%p\n",names[0]+1);
	//names[0]+1表示的是第一个一维数组中第二个元素的地址,正向偏移一个储存单元
	printf("print the adress of the expression :%p\n",names[0]+3);
	//由于每个一维数组之间的地址都是相连的,所以可以这样表示,这个表示的是第二个一维数组中第一个元素的地址
	//和第二个数组的地址是相同的
	
	return	0; 
	
	
}

打印结果如下:

print the adress of the point names :000000000062FDF0
print the adress of the point names[0]:000000000062FDF0
print the adress of the expression :000000000062FDFC
print the adress of the expression :000000000062FDF4
print the adress of the expression :000000000062FDFC

2.指向数组的指针进行解引用:

首先:我们来分析指向数值的指针进行解引用,得到的结果是这个指针储存的地址上面的数据。

那么:类比上面,指向数组的指针进行解引用,得到的结果就应该是这个指针储存的地址上面的数据。

继续:我们通过解引用的方式来找寻答案:二维数组名是是第一个一维数组的地址

因此:满足names=&names[0],在两边同时使用解引用运算符*

*names= *&names[0]=names[0] //所以 *names和names[0]是完全相同的。

可以发现,对指向数组的指针进行解引用,得到的结果是一个地址,这个地址是所指向的数组的首元素的地址。

因此:* *names= *names[0]=names[0] [0]

可以发现,对指向数组的指针进行双重解引用,得到的结果是所指向数组的首元素的值。

//对进阶指针进行解引用以及双重解引用
#include	<stdio.h>

int	main(void)
{
	int names[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
	
	int (*ptint)[3]=&names[2];//声明一个指向拥有三个int型元素的数组 ,随后把一个拥有三个int型元素的数组的地址赋值给它
	//也就是指向二维数组中的第三个一维数组的指针 
	
	printf("print the adress of the point names :%p\n",names);
	printf("print the adress of the point ptint :%p\n",ptint);
	
	printf("open the point names :%p\n",*names);
	printf("print the adress of names[0]:%p\n",names[0]);
	printf("print the adress of &names[0][0]:%p\n",&names[0][0]);
	printf("print the value of the names[0][0]:%d\n",names[0][0]);
	printf("double open the point names:%d\n",**names);
	
	printf("open the point ptint :%p\n",*ptint);
	printf("print the adress of ptint[0]:%p\n",ptint[0]);//这里也有一个很重要的区分就是,如果指针指向的是数值,那么ptint[0]表示的
	//就是首元素的值,如果指针指向的是数组,那么ptint[0]表示的就是首元素的地址,因为ptint[0]就等价于*ptint
	printf("print the adress of ptint[0][0]:%p\n",&ptint[0][0]);//ptint[0][0]表示的就是第一个数组中的第一个元素 
	printf("print the value of the ptint[0][0]:%d\n",ptint[0][0]);
	printf("double open the point ptint:%d\n",**ptint);
	
	return	0;
 } 

程序运行结果如下:

print the adress of the point names :000000000062FDE0
print the adress of the point ptint :000000000062FDF8
open the point names :000000000062FDE0
print the adress of names[0]:000000000062FDE0
print the adress of &names[0][0]:000000000062FDE0
print the value of the names[0][0]:1
double open the point names:1
open the point ptint :000000000062FDF8
print the adress of ptint[0]:000000000062FDF8
print the adress of ptint[0][0]:000000000062FDF8
print the value of the ptint[0][0]:7
double open the point ptint:7

3.接受二维数组的指针形参

接受二维数组的指针形参,例如:int (*ptint)[4];表示的是接受一个指向含有4个整形元素的数组的指针或者一个含有四个整形元素的数组的地址。

通过程序来理解:

有时候,为了方便,我们把二维数组看成有行有列的数组,一维数组的个数相当于行数,一维数组中元素的个数相当于列数,可以求一行中,各元素之和,或者一列中,各元素之和。

(原因是二维数组初始化成这样:

{

{1,2,3,4,5}

{2,3,4,5,6}

{4,5,6,7,8}

};

//处理二维数组
#include	<stdio.h>
#define ROWS 3
#define COLS 4//在程序中定义的宏,在每个参数中都能够用到 因此我所定义的函数只需要能够接受指向数组的指针这一个参数。 

void sum_rows(int (*ptint)[COLS]);
void sum_cols(int (*ptint)[COLS]);
int sum_elements(int (*ptint)[COLS]);//一定要注意,在声明指向数组的指针的时候,一定不要忘记后面的数组中元素的个数。 

int	main(void)
{
	int junk[ROWS][COLS]={{2,4,6,8},{3,5,7,9},{12,10,8,6}};
	int sum=0;
	sum_rows(junk);
	sum_cols(junk);
	sum=sum_elements(junk); 
	printf("print the sum of the elemens in the array :%d\n",sum);
	
	return	0; 
} 

void sum_rows(int (*ptint)[COLS])//这个函数是用来计算二维数组中,各行元素之和,因此外层循环的是行,内层循环的是列。 
{ 
	int i;
	int total;
	int n;
	for (i=0;i<ROWS;i++)
	{
		total=0;//这个total=0很关键,因为每求完一行,都需要将total归零。 
		for (n=0;n<COLS;n++)
		{
			total+=*(*(ptint+i)+n);//这个指针的用法是重点,ptint是指向第一个数组的指针,在每一次内层循环中,i的值都不变 
		}//ptint+i就表示所指向的一维数组,对其解引用表示这个一维数组中首元素的地址,+n表示这个一维数组中的其他元素的地址
		//通过解引用,就遍历了当前的一维数组,然后在进入第二个外层循环,以此类推。 
		printf("print the %d row elements'sum : %d\n",i+1,total);
	}
	return;
}
void sum_cols(int (*ptint)[COLS])//这个函数是用来计算这个二维数组中,各列元素之和,因此外层循环是列,内层循环是行。
{
	int i;
	int n;
	int total;
	for (i=0;i<COLS;i++)
	{
		total=0;
		for (n=0;n<ROWS;n++)
		{
			total+=*(*(ptint+n)+i);//这个指针很关键,ptint是第一个数组的地址,n是不断改变的因此ptint+n表示的就是这三个数组的地址, 
		}//i在每一次内层循环中都不变,行在变,列不变,因此每一次内层循环都是求一列的和。 
		printf("print the %d cols elements'sum : %d\n",i+1,total);
	}
	return;
}

int sum_elements(int (*ptint)[COLS]) 
{
	int i;
	int n;
	int total;
	for (i=0,total=0;i<ROWS;i++)  
	{
		for (n=0;n<COLS;n++)//由于每次进入外层循环total不清零,因此求出来的就是总和。 
		{
			total+=*(*(ptint+i)+n); 
		}
	}
	return	total;	
}

打印结果如下:

print the 1 row elements'sum : 20
print the 2 row elements'sum : 24
print the 3 row elements'sum : 36
print the 1 cols elements'sum : 17
print the 2 cols elements'sum : 19
print the 3 cols elements'sum : 21
print the 4 cols elements'sum : 23
print the sum of the elemens in the array :80

由于接受一位数组的指针形参有数组形式的写法,因此接受二维数组的指针形参也有数组写法:

//接受二维数组的指针形参的数组写法
#include	<stdio.h>
#define ROWS 3
#define COLS 4

void sum_rows(int array[][COLS]);
void sum_cols(int array[][COLS]);
int sum_elements(int array[][COLS]);

int	main(void)
{
	int junk[ROWS][COLS]={{2,4,6,8},{3,5,7,9},{12,10,8,6}};
	
	sum_rows(junk);
	sum_cols(junk);
	int sum=sum_elements(junk);
	
	printf("print the array'elemens sum : %d\n",sum);
	
	return	0;
}

void sum_rows(int array[][COLS])//只有第一个方括号是空的才代表这是一个指针,只不过用的是数组写法 
{
	int i;
	int n;
	int total;
	for (i=0;i<ROWS;i++)
	{
		total=0;
		for (n=0;n<COLS;n++)
		{
			total+=array[i][n];//我们可以发现,数组形式非常的方便,array[][]每个里面都不是空的,就代表最基本的元素 
		}
		printf("print the %d row elements sum : %d\n",i+1,total);
	}
	return;
}

void sum_cols(int array[][COLS])
{
	int i;
	int n;
	int total;
	for (i=0;i<COLS;i++)
	{
		total=0;
		for (n=0;n<ROWS;n++)
		{
			total+=array[n][i];
		}
		printf("print the %d cols elements sum : %d\n",i+1,total);
	}
	return;
}

int sum_elements(int array[][COLS])
{
	int i;
	int n;
	int total=0;
	for (i=0;i<ROWS;i++)
	{
		for (n=0;n<COLS;n++)
		{
			total+=array[i][n];
		}
	}
	return	total;
}

打印结果如下:

print the 1 row elements sum : 20
print the 2 row elements sum : 24
print the 3 row elements sum : 36
print the 1 cols elements sum : 17
print the 2 cols elements sum : 19
print the 3 cols elements sum : 21
print the 4 cols elements sum : 23
print the array'elemens sum : 80

我们可以发现,无论是一维数组还是二维数组,利用数组形式来表示接受数组的指针形参都是非常方便的。

5)指针数组

指针数组指的是数组元素是指针的数组,指针数组不需要初始化。

声明方法:int *ptint[20];

表示的是:声明了20个int类型的指针(并且是指向数值的指针)。

接受指针数组的形式参数:

设置方法:int *ptint[]; 这样就表示一个接受指针数组的形式参数,接受的是数组中第一个元素的地址,也就是第一个指针的地址。

posted @ 2021-12-17 16:01  OldSword-JY  阅读(96)  评论(0)    收藏  举报