C语言------指针(2)

前面已经向大家介绍了指针的一些基本内容,接下来,就在再我来先大家讲解一下指针的其他内容。

1. 数组名的理解

cs 复制代码
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

在学习数组的过程中,我们肯定会写过以上代码,我们知道 int 是该数组的数据类型,10 是该数组的大小,arr是数组名,那数组名代表这什么吗?它除了是数组的名字之外,还有其他意义吗?

接着还是用代码来探索其意义

cs 复制代码
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("arr=%p", arr);
	printf("arr[0]=p", &arr[0]);
	return 0;
}

我们写出以上代码并运行,分别打印其地址,运行后我们惊奇的发现arr的地址和arr0的地址恰好一样,而arr0有恰好是首元素。由此。我们得出数组名不仅仅是数组的名字,其也是数组首元素的地址。

那是不是在所有情况下,数组名都是首元素地址吗?

答案肯定不是,有两种情况除外。

(1)sizeof(arr) ,当我们用sizeof()来计算数组的大小时,此时数组名代表的是整个数组。

我们还是用代码来直观感受一下

cs 复制代码
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr);
	printf("arr=%d\n", sz1);
	int sz2 = sizeof(arr[0]);
	printf("arr[0]=%d", arr[0]);
	return 0;
}

用sz1来存储arr的大小,用sz2存储arr0的大小( 数组中一个元素的大小)。

运行代码

发现sz1的值为40,恰好是整个数组的大小,说明此时数组名代表整个数组。

(2)&arr 当数组名放在取地址操作符后面时,此时数组名也代表着整个数组。

我们还是用代码直接感受

cs 复制代码
int* p1 = &arr;
int* p2 = &arr[0];
printf("&arr  =%p\n", &arr);
printf("&arr+1=%p\n", arr + 1);
printf("&arr[0]  =%p\n", &arr[0]);
printf("&srr[0]+1=%p\n", &arr[0] + 1);

分别对arr和arr0取地址,分别将其存于指针变量p1和p2中

运行代码发现,分别对其加1时,指针p1跳过了40个字节,而指针p2却只跳过了一个字节。

前面我们学过,指针变量的类型绝定了其加1减1,一次能跳过的大小。

所以只有当指针p1指向整个数组时,p1才可能一次跳过40个字节。所以当数组名在&操作符后面时,此时数组名也代表整个数组。

除了这两种情况外,数组名都代表数组首元素地址。

2.使用指针访问数组名

既然数组名可以代表首元素地址,那么就可以用指针变量来保存其地址。

所以当我们用数组名作为一个函数的实参时,其形参可以写成数组的形式也可以用指针变量来接收。

我们还是用代码直接感受一下

复制代码
int main()
{
	int arr[5] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		scanf("%d ", p + i); //这里arr本身就有 & 含义了,所以不用在家 &
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

从上面代码可以看出*(p+i)和arri是等价的。

其实 *(p+i)写成pi也是可以运行的,所以说本质上*(p+i)是等同于pi的。

有个小技巧,我们可以将 看成一个解引用操作符就好理解了。

总结:数组的访问,是以首元素地址的基础上进行的偏移量进行访问的。

3.一维数组传参的本质

当我们将一个一维数组传递给一个函数的时候,本质上如何传递的呢?接下来探讨一下。

以代码为例

复制代码
void test(int arr[])
{
 int sz2 = sizeof(arr)/sizeof(arr[0]);
 printf("sz2 = %d\n", sz2);
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int sz1 = sizeof(arr)/sizeof(arr[0]);
 printf("sz1 = %d\n", sz1);
 test(arr);
 return 0;
}

从代码运行结果发现,sz1的值和sz2的值并不相同。这是为什么呢?

哦,原来当我们将一个数组名传过去的时候,其实是传过去了一个地址,则形参的类型就是一个指针类型,所以当我们在test()函数里面计算arr的大小时,其实并不是计算数组的大小,而是计算了一个指针的大小。正因为函数部分参数是一个指针,所以导致我们无法在函数内部计算数组的大小。

总结:一维数组传参时,形参部分可以写成数组形式,也可以写成指针形式。

4.二级指针

既然变量都有属于自己的地址,那么指针变量也有属于它自己的地址,那要用什么来存储指针变量呢?

那便是二级指针。

这就是一个二级指针和一个一级指针的关系图

当我们对二级指针进行解引用,得到的是一级指针的地址,再对二级指针进行一次接应用,我们就可以得到一级指针里面保存的内容。

5.指针数组

紧接着我们再来将一个特别的数组---------指针数组。

通俗易懂,指针数组就是一个用来存放指针的数组。

6.指针数组模拟二维数组

cs 复制代码
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* parr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

分析代码:

首先我们要清楚,二维数组本质上可以看成是有多个一维数组组成的,这里我们将每个数组的数组名都存储在一个指针数组中,其中arr1,arr2,arr3 分别是指针数组中的第1个,第2个,第3个元素,前面我们提到 可以看成解引用操作符。假如我们对parr解引用一次,便得到了arr1,也就是arr1的首元素地址,在对其进行一次解引用,就进一步的到了arr1里面的内容。

简单来说,parr1就等于arr1,parr1i就相当于arr1j。后面的以此类推。

代码运行如下

以上就是我对数组名的理解。谢谢大家的观看。

相关推荐
下午写HelloWorld15 小时前
后量子密码算法:协同签名研究综述
算法·密码学·后量子·协同签名
小蒋学算法15 小时前
算法-计算右侧小于当前元素的个数-分治&归并思想
java·数据结构·算法
阿狸猿15 小时前
论企业应用系统的分层架构风格
java·开发语言·架构
JAVA96515 小时前
JAVA面试-并发篇 07-CAS底层原理是什么有什么缺陷如何解决
java·开发语言·面试
lqqjuly15 小时前
FlashAttention 深度解析
人工智能·深度学习·算法
San813_LDD15 小时前
[QT]Qt对象树笔记:父子关系与内存管理
开发语言·qt
gaohe26AIliuzeyu16 小时前
Java接口
java·开发语言
满怀冰雪16 小时前
第05篇-滑动窗口算法-一套模板解决子串与子数组问题
java·算法
码云骑士16 小时前
【3.1Java基础】Java运算符常见错误排查:10个高频编译运行错误一网打尽
java·开发语言
小程故事多_8016 小时前
RAGFlow 分块策略全景与 Book 策略深度解析
java·开发语言·rag