C语言:指针与数组

一、. 数组名的理解

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

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

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


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

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

    可以当我们试着打印这俩个地址时,看出来结果似乎是相同的。那到底是有什么区别呢,我们再想想看指针变量类型不同的区别,是否有些思路了呢。
    这里我们发现arr和arr+1相差4个字节,所以&arr0和&arr0+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相当于&arri。在打印输出时,也是相同的原理,依次取出每个元素的地址并解引用即可,也就是*(arr+i)相当于arri。

三、一维数组传参的本质

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环境下它就占八个字节。而在它后面的arr0就像前面说的一样,相当于*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指针数组中存放的是数组名,也就是每行首元素的地址。我们打印时使用的arrij其实是通过arri,也就是arr+i找到是哪个小数组;再通过arrij,就是*(arrn+j),也就是*(*(arr+i)+j)就能找出该行的每个数了。上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的。

相关推荐
wuminyu1 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
Felven1 小时前
B. Reverse a Permutation
c语言
Logic1012 小时前
C语言/数据结构排序题解:三个数的最大乘积——排序后比较两种候选情况
c语言·数据结构·数组·排序·时间复杂度·算法题·负数处理
无忧.芙桃2 小时前
位图妙用:512MB 实现 40 亿整数的存在性查询
c语言·c++·面试·哈希算法
听风若依2 小时前
数据在内存中的存储-纸面笔记整理
c语言·笔记
殷色玫瑰3 小时前
C++ string类详解:常用接口、字符串操作与模拟实现
java·linux·c语言·开发语言·数据结构·c++
Code_Solitude3 小时前
C语言:关于二维数组的作业总结
java·c语言·前端
老花眼猫4 小时前
数学艺术图案画-曼陀罗(93)
c语言·经验分享·青少年编程·课程设计
水饺编程5 小时前
第1章:下载 Linux 0.12 内核
linux·c语言·汇编·ubuntu
沐晓时光5 小时前
C语言入门,深入理解指针(2)
c语言·开发语言