C语言知识回顾——指针与数组

今天萌新博主来给大家介绍C语言指针中,指针与数组的部分,在这一块也是非常难以理解的,闲言少叙,咱们开始吧!

大家都知道数组与指针有着紧密的联系,数组是由一组若干个元素构成,我们在访问数组的时候,采用的是循环体的方式,就是把数组的下标逐步的循环一次,这样我们就可以读出所有数组元素的值。那么重点来了,数组下标与地址的之间的关系,如果我们掌握好,大家学起来指针与数组就会非常的吃力,萌新博主与大家也是感同身受啊!

【数组的存储方式】
在这里插入图片描述
数组的三个特点:
1.数组有若干个元素
2.元素的数据类型必须相同
3.数组中的数据元素是有序排列的

arr1[4]={1,2,3,4}
arr2[4]={4,3,2,1}
//这两个数组不一样

数组元素占用的字节长度都是相等的;
指向数组的指针的初始值就是数组的首地址;

int arr[4]={1,2,3,4}

每个元素占用的字节长度为:sizeof(int);

若sizeof(int)==2,假设数组的第一个元素a[0]的地址为0x00000001,则数组的第二个元素a[1]的地址为0x00000003,则数组的第三个元素a[2]的地址为0x00000005

所以说数组中的数据元素是有序排列的,那我们在查找数组元素的时候,只需要查找到第一个元素的地址,就能按照顺序找到相应的元素了。

#include <stdio.h>
int main(void)
{
	int arr[4]={1,2,3,4};
	printf("arr[0]=%p\n",&arr[0]);
	printf("arr[1]=%p\n",&arr[1]);
	printf("arr[2]=%p\n",&arr[2]);
	printf("arr[3]=%p\n",&arr[3]);
	return 0;
}

运行结果:
在这里插入图片描述
那我们再来看指针的性质:
【指针与整数的运算】
指针加n=指针原来的值+nsizeof(指针所指变量类型)
指针减n=指针原来的值-n
sizeof(指针所指变量类型)

int *p=NULL
p=&arr[0]
p0x000001
p+1
0x000001+1×sizeof(int)
p+20x000001+2×sizeof(int)
p+3
0x000001+3×sizeof(int)

#include <stdio.h>
int main(void)
{
	int arr[4]={1,2,3,4};
	int *p=NULL;//声明一个指针
	int i=0;
	p=&arr[0];//等价于p=arr;
	printf("sizeof(int)=%d\n",sizeof(int));
	printf("arr[0]=%p\n",&arr[0]);
	printf("arr[1]=%p\n",&arr[1]);
	printf("arr[2]=%p\n",&arr[2]);
	printf("arr[3]=%p\n",&arr[3]);
	for(i=0;i<=3;i++)
	{
		printf("p+%d=%p\n",i,p+i);//循环输出指针的值
	}
}
结果:
sizeof(int)=4
arr[0]=0019FF20
arr[1]=0019FF24
arr[2]=0019FF28
arr[3]=0019FF2C
p+0=0019FF20
p+1=0019FF24
p+2=0019FF28
p+3=0019FF2C

我们不难发现,我们可以通过指针来访问一维数组在这里插入图片描述
注意:以上对应关系的成立,指针p必须指向数组元素的首地址。

【那指针是如何访问数组的呢?】

#include <stdio.h>
int main(void)
{
	int arr[8]={1,2,3,4,5,6,7,8};
	int i=0;
    int *p=NULL;
    p=arr;//指针指向首地址
	printf("arr[i]\n");
    for(i=0;i<8;i++)
	{
	printf("arr[%d]=%d\n",i,arr[i]);
	}

    printf("p+i\n");
	for(i=0;i<8;i++)
	{
	printf("p+%d=%d\n",i,*(p+i));
	}
    return 0;
}

在这里插入图片描述
【访问指针的四种方法】
1.直接下标法
有一个数组a[i]
int *p=a等价于int *p=&a[0]

2.首地址自加法*(a+i)
数组名就是首地址:a等价于p

3.指针自加法*(p+i)

4.指针下标法p[i]
在这里插入图片描述
通过指针可以修改数组元素

int arr1[8]={省略};
int arr2[8]={省略};
int i=1;
int *p1=arr1;//使指针p指向arr的首地址
int *p2=arr2;
p1=p2;//改变指针指向
p++;//让指针指向下一个元素

注意:指针可以自加,但是数组名不可以自加,如arr1++,同时数组名也不能相互赋值,如arr1=arr2;

【用数组访问与用指针访问的区别】
1.访问方式不同

使用数组名访问时直接访问,使用指针是间接访问;
下标从0开始的原因:因为下标的实质是地址偏移量

2.占用空间不同

系统为arr分配了“sizeof(类型)*元素个数”个字节,给指针分配了“sizeof(类型)”个字节

换句话说使用指针可以降低空间复杂度,我们来看个程序:

#include <stdio.h>
int main(void)
{
	int arr[8]={1,2,3,4,5,6,7,8};
    int *p=NULL;
    p=arr;//指针指向首地址
	printf("sizeof(arr)=%d\n",sizeof(arr));
	printf("sizeof(*p)=%d\n",sizeof(*p));
	return 0;
}

在这里插入图片描述
很明显,系统为arr分配了32个字节,给指针分配了4个字节。

【让我们来回顾二维数组】
二维数组a[i][j],具有两个下标:行下标与列下标
存储方式:先存储行下标,再存储列下标
即先在一个行下标内把a[0]列存满,再存a[1]列的元素。

a[0][0],a[0][1],a[0][2],a[0][3]
a[1][0],a[1][1],a[1][2],a[1][3]

【二维数组arr[][]的首地址有多种表达形式】
1.将二维数组看成元素是一维数组的元素
2.数组中第0行第0列的地址,&arr[0][0]
3.由于二维数组先存储行,所以第一行的第一个元素的地址,即&a[0]也是首地址

二维数组的元素个数:i×j

arr[2][3]={(1,2,3),(4,5,6)}
arr[2][3]={arr1[3],arr2[3]}
二维数组的首地址:&arr[0][0]&arr[0]
二维数组的数组名,不是首地址
int arr[i][j];
int *p=&arr[0][0];

此时p指向第一行第一个元素,即arr[0][0],而列指针是arr[i],指向第(i+1)列的元素。

不允许操作:p=arr或声明的时候使用int *p=arr(arr会指向一整行的地址)

那我们从a[0][0]跳到a[2][2],如何操作?

#include <stdio.h>
int main(void)
{
	int a[3][3]={1,2,3,4,5,6,7,8,9};
	int i=0;
	int j=0;
	for(i=0;i<3;i++)
	{
	for(j=0;j<3;j++)
	{
	printf("a[%d][%d]=%d\t",i,j,a[i][j]);
	}
	printf("\n");
	}
}

在这里插入图片描述
a[0][0]是第一行第一列的元素 ,p=&[0][0]
a[2][2]是第三行第三行的元素

有一个指针p指向二维数组的首地址:&a[0][0]
指针的偏移量为:p+行数×行下标+列下标;

我们来看个例子:

#include <stdio.h>
int main(void)
{
	int a[3][3]={1,2,3,4,5,6,7,8,9};
	int i=0;
	int j=0;

	int *p=NULL;
	p=&a[0][0];
	
	for(i=0;i<3;i++)
	{
	for(j=0;j<3;j++)
	{
	printf("a[%d][%d]=%d\t",i,j,a[i][j]);
	}
	printf("\n");
	}

	for(i=0;i<3;i++)
	{
	for(j=0;j<3;j++)
	{
    printf("p+N*%d+%d\t",i,j,*(p+3*i+j));
	}
	printf("\n");
	}
    printf("p+N*i+j\n");
}

在这里插入图片描述
于是我们得到二维数组的四种表现形式,如下图
在这里插入图片描述
【二维数组的矩阵转置】

#include <stdio.h>
int main(void)
{
	int a[3][3]={1,2,3,4,5,6,7,8,9};
	int i=0;
	int j=0;
	int *p=NULL;
	p=&a[0][0];//p指向首地址

	printf("转置前:\n");
		for(i=0;i<3;i++)
		{
			for(j=0;j<3;j++)
			{
			printf("a[%d][%d]==%d\t",i,j,*(p+3*i+j));
			}
			printf("\n");
		}

		printf("转置后:\n");
		for(j=0;j<3;j++)
		{
			for(i=0;i<3;i++)
			{
			printf("a[%d][%d]==%d\t",i,j,*(p+3*i+j));
			}
			printf("\n");
		}
}

在这里插入图片描述
在for循环中,我们只需要把i换成j,j换成i就可以实现矩阵的转置,而不需要中间变量。

【二维数组形参的形式】
另外当我们用数组作为函数的形式参数,我们需要给函数传递一个数组首地址和一个数组长度

【将数组和数组长度分别作为形参】

void function(int array[],int size)
void function(int *arry,int size)

我们再来一个例子
【在一个升序数组中查找大于3的元素】

#include <stdio.h>
void printarray(int *arr,int n)
{
	int i=0;
	for(i=0;i<n;i++)
	{
	printf("%4d",arr[i]);
	}
	printf("\n");
}


int main(void)
{	
	int arr[9]={1,2,3,4,5,6,7,8,9};
	int *p=NULL;
	p=arr;
	while(*p<3)//while循环对指针p进行取值
	{
	p++;//如果p的值<3,则将地址下移,再继续与3比较
	}
	printarray(p,9-(p-arr));//经过3次自加后,指针由指向元素1到指向元素4
	//那么指针p将以arr[3]为首地址传到函数中,(p-arr)为arr[3]与首地址arr[0]的差值,也就之间的个数
	return 0;
}

在这里插入图片描述
【用二维数组形参函数来完成矩阵倒置】

#include <stdio.h>
void function1(int *p,int M,int N)
{
	int i,j;
	for(i=0;i<M;i++)
	{
		for(j=0;j<M;j++)
		{
		printf("%d",*(p+i*N+j));//也可以用*p++:输出当前值,地址下移
		}
		printf("\n");
	}
	
}

void function2(int *p,int M,int N)
{
	int i,j;
	for(j=0;j<M;j++)
	{
		for(i=0;i<M;i++)
		{
		printf("%d",*(p+i*N+j));//*(p+i*N+j)是指针偏移量法
		}
		printf("\n");
	}
		
}
//注意形参M,N是行列长度,不是下标

int main(void)
{	
	int arr[3][3]={1,2,3,4,5,6,7,8,9};
	int *p=NULL;
	p=&arr[0][0];
	function1(p,3,3);
	printf("\n");
	function2(p,3,3);
	return 0;
}

在这里插入图片描述
二维数组形参的其他形式
在这里插入图片描述
数组首地址表示法
在这里插入图片描述

发布了21 篇原创文章 · 获赞 44 · 访问量 1579

猜你喜欢

转载自blog.csdn.net/weixin_43964458/article/details/104496115