【C语言指针专题(4)】指针与一维数组

发布时间:2023年12月21日

一、数组名的理解

在之前我们我们使用指针访问数组的时候,使用到了这样一段代码:

int arr[10] = { 0 };
int* pa = &arr[0];

这里我们使用 &arr[0] 的方式拿到了数组第一个元素的地址,但是其实数组名本来就是地址,而且 是数组首元素的地址,我们来做个测试。

//演示1.1

#include <stdio.h>

int main()
{
    int arr[10] = { 0 };
    int* pa = &arr[0];
    int* pb = &arr;
    printf("%p\n", pa);
    printf("%p\n", pb);
    return 0;
}

\

我们发现数组名和数组首元素的地址打印出的结果一模一样,数组名就是数组首元素(第一个元素)的地址。 这时候我们可能会有疑问,如果数组名是数组首元素的地址,那下面的代码要怎么理解呢?

//演示1.2

#include <stdio.h>

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

如果数组名是数组首元素的地址,那么这里的结果应该是4或8,也就是一个地址的大小。但我们已经知道结果是40,是10个整型的大小。这是为什么呢?

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

? sizeof(数组名),sizeof中单独放数组名,这?的数组名表示整个数组,计算的是整个数组的大小,单位是字节。

? &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的,下面我们会详细解释)。

除此之外,任何地方使用数组名,数组名都表示首元素的地址。

接下来让我们看看下面这段代码:

//演示1.3

#include <stdio.h>

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

从这段代码看,&arr[0]、arr、&arr的打印结果完全一样,那么它们区别在哪里呢?

我们再看下面这段代码:

//演示1.4

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    printf("    &arr[0] = %p\n",&arr[0]);
    printf("&arr[0] + 1 = %p\n",&arr[0] + 1);
    printf("    arr + 1 = %p\n",arr + 1);
    printf("   &arr + 1 = %p\n",&arr + 1);
    return 0;
}

它们的结果已经不一样了,可以看到,相比于arr[0],

&arr[0]+1和arr+1,跳过了4个字节,说明它们代表的是数组首元素的地址,+1后跳过一个元素,也就是跳过一个整型;

&arr+1,跳过了40个字节,说明它代表的是整个数组的地址,+1后跳过一个arr数组的大小。

二、使用指针访问数组

有了前面知识的理解,再结合数组的特点,我们就可以很方便的使用数组来访问数组了。

//演示2.1

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* parr = arr;
    //输入
	for (i = 0; i < sz; i++)
	{
		scanf("%d", parr + i);
        //也可以写成 scanf("%d", arr + i);
	}
    //输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(parr + i));
	}
	return 0;
}

数组名arr是数组首元素的地址,可以赋值给parr,也就是说数组名arr和整型指针parr在这里是等价的。那么我们可以使用arr[i]来访问数组中的元素,是不是也可以使用parr[i]来实现这样的操作呢?

//演示2.2

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* parr = arr;
	for (i = 0; i < sz; i++)
	{
		scanf("%d", parr + i);
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", parr[i]);
	}
	return 0;
}

输入的数据被很好的打印了出来,所以说parr[i]是等价于arr[i]的,实质上,arr[i]和parr[i]都会被计算机理解为*(parr+i)。

三、一维数组传参的本质

我们已经了解两个知识,在主函数中通过sizeof相除来求数组中元素的个数,以及数组可以作为参数传递给函数。

那么我们可不可以把一个数组传递给主函数外的某个函数,然后在这个函数内通过sizeof相除的方法求出数组元素的个数呢?我们来试一下:

//演示3.1

#include <stdio.h>

void test(int pa[])
{
    int sz2 = sizeof(pa)/sizeof(pa[0]);
    printf("sz2 = %d\n", sz2);
}

int main()
{
    int arr[6] = { 0 };
    int sz1 = sizeof(arr)/sizeof(arr[0]);
    printf("sz1 = %d\n", sz1);
    test(arr);
    return 0;
}

我们发现这种做法行不通。它的原因涉及到一维数组传参的本质,我们接下来来探讨一下这个问题。

我们知道,数组名代表的是数组首元素的地址,那么我们在将数组名作为参数直接传递给函数时,传过去的也就应该是数组首元素的地址。

所以在演示3.1中test函数计算sz2时,sizeof(pa)的值不会是arr数组的大小,而是arr数组首元素地址的大小,也就是4或8个字节,所以sz2结果的结果是1或2(在x64环境下)。

通过上面的例子,我们知道了一维数组在传参时实际传递的是一个地址,所以函数的形参部分理论上应该用一个指针来接受传来的参数。但是实际运用中呢,我们常常就写作数组的形式了,因为这样并不错,而且也更易于理解。

文章来源:https://blog.csdn.net/jk23728/article/details/134615597
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。