C语言:指针与数组

一、. 数组名的理解

复制代码
int arr[5] = { 0,1,2,3,4 };
int* p = &arr[0];

在之前我们知道要取一个数组的首元素地址就可以使用&arr[0],但其实数组名本身就是地址,而且是数组首元素的地址。在下图中我们就通过测试看出,结果确实如此。

可是我们再来看下图的结果,我们发现当我们对数组名使用sizeof函数是会发现不同之处,如果数组名就是地址,那么再x64环境下应该就是八个字节,但是却输出了20,这是为什么呢?


其实数组名就是数组首元素(第⼀个元素)的地址是对的,但是有两个例外:

  1. sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小, 单位是字节。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素 的地址是有区别的)。

    可以当我们试着打印这俩个地址时,看出来结果似乎是相同的。那到底是有什么区别呢,我们再想想看指针变量类型不同的区别,是否有些思路了呢。
    这里我们发现arr和arr+1相差4个字节,所以&arr[0]和&arr[0]+1相差4个字节,是因为&arr [0]和 arr 都是首元素的地址,+1就是跳过⼀个元素。但是&arr 和 &arr+1相差20个字节。
    因为&arr是数组的地址,+1 操作是跳过整个数组的。到这里大家应该搞清楚数组名的意义了吧。
    二、使用指针访问数组
python 复制代码
#include <stdio.h>
int main()
{
	int arr[5] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		scanf("%d", arr + i);
	}
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}
	return 0;
}

在scanf()中arr是arr数组首元素的地址。因为数组元素的地址是递增的,所以随着这个地址的增加,出现的地址就变成了数组中其他元素的地址了,也就是arr+i相当于&arr[i]。在打印输出时,也是相同的原理,依次取出每个元素的地址并解引用即可,也就是*(arr+i)相当于arr[i]。

三、一维数组传参的本质

python 复制代码
#include <stdio.h>
void test(int arr[5])
{
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("%d ", sz1);
}
int main()
{
	int arr[5] = { 0 };
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	test(arr);
	printf("%d ", sz2);
	return 0;
}

当我们把数组传给函数去实现求其中的元素数时,我们会发现得到的结果并不是我们想要的结果。这是为什么呢?我们接下来分析一下这段代码并想一想一维数组传参的本质。在一维数组传参中我们传的其实是这个数组的首元素的地址,所以在test()中得到的是arr这个数组中第一个元素的地址,在x64环境下它就占八个字节。而在它后面的arr[0]就像前面说的一样,相当于*arr,也就是arr数组的首元素的值,因为它的类型是int,所以占四个字节。所以一维数组传参的本质就是传递的是指针,也就解决不了求元素数的问题。当然在传参时我们也可以写成指针的形式。

python 复制代码
#include <stdio.h>
void test(int* arr)
{
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1=%d ", sz1);
}
int main()
{
	int arr[5] = { 0 };
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	test(arr);
	printf("sz2=%d ", sz2);
	return 0;
}

四、冒泡排序

其实冒泡排序的核心就是两两相邻元素比较。如果我们要将一个数组中的数从小到大排列就可以使用冒泡排序。

python 复制代码
#include <stdio.h>
void bubble_sort(int* arr,int sz)
{
	int temp;
	for (int j = 1; j < sz; j++)
	{
		for (int i = 1; i <= sz - j; i++)
		{
			if (*(arr + i - 1) > *(arr + i))
			{
				temp = *(arr + i - 1);
				*(arr + i - 1) = *(arr + i);
				*(arr + i) = temp;
			}
		}
	}
}
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		scanf("%d",&arr[i]);
	}
	bubble_sort(arr,sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

当然如果该数列的元素如果本来就是正序排列的,那么这样做就会很浪费效率,我们可以对程序再进行优化一下。

python 复制代码
#include <stdio.h>
void bubble_sort(int* arr,int sz)
{
	int temp;
	for (int j = 1; j < sz; j++)
	{
		int flag = 1;
		for (int i = 1; i <= sz - j; i++)
		{
			if (*(arr + i - 1) > *(arr + i))
			{
				flag = 0;
				temp = *(arr + i - 1);
				*(arr + i - 1) = *(arr + i);
				*(arr + i) = temp;
			}
		}
		if (flag)
			break;
	}
}
int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		scanf("%d",&arr[i]);
	}
	bubble_sort(arr,sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在程序中加入了flag变量,如果在第一次排序中没有改变排序,也就是这个数组的数本来就为正序,就会跳出这个循环。

五、二级指针

我们知道指针变量也是变量,那么它也应该有地址,什么能储存它呢,就是二级指针。

如图所示,pa是a的指针变量,我们对pa进行取地址,也就是我们刚才说的二级指针。
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa 。
**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a 。
六、指针数组

从名字中我们就能看出这是存放指针的数组。指针数组的每个元素都是用来存放地址(指针)的。

接下来,我们用指针数组模拟二维数组。

python 复制代码
#include <stdio.h>
int main()
{
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,3,4,5 };
	int arr3[4] = { 3,4,5,6 };
	int* arr[3] = {arr1,arr2,arr3};
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

我们能看出来打印出的样子和二维数组一模一样。这是怎么实现呢?arr指针数组中存放的是数组名,也就是每行首元素的地址。我们打印时使用的arr[i][j]其实是通过arr[i],也就是arr+i找到是哪个小数组;再通过arr[i][j],就是*(arr[n]+j),也就是*(*(arr+i)+j)就能找出该行的每个数了。上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的。

相关推荐
TDengine (老段)38 分钟前
TDengine C/C++ 连接器入门指南
大数据·c语言·数据库·c++·物联网·时序数据库·tdengine
JAY_LIN——82 小时前
C语言内存函数memcpy、memmove、menset、mencmp
c语言·开发语言
松涛和鸣2 小时前
DAY47 FrameBuffer
c语言·数据库·单片机·sqlite·html
福楠2 小时前
C++ STL | list
c语言·开发语言·数据结构·c++·算法·list
myloveasuka2 小时前
int类型的取值范围(为什么负数比正数表示的范围多一位)
c语言·c++
程序员zgh3 小时前
类AI技巧 —— 文字描述+draw.io 自动生成图表
c语言·c++·ai作画·流程图·ai编程·甘特图·draw.io
黎雁·泠崖3 小时前
栈与队列之栈入门攻略:从核心概念到数组实现
c语言·数据结构
福楠3 小时前
模拟实现list容器
c语言·开发语言·数据结构·c++·list
越努力^越幸运4 小时前
C中部分的字符函数
c语言
Benny的老巢4 小时前
Chrome 缓存迁移教程:5种方法解决 C 盘空间不足问题(2026年最新)
c语言·chrome·缓存