4 min to read
椋鸟C语言笔记#19
数组名、指针访问数组、一维数组传参的本质
萌新的学习笔记,写错了恳请斧正。
数组名的理解
数组名其实就是数组首元素的地址,只有两个例外(马上讲)
我们不妨写一个程序验证一下:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n", &arr[0]);
printf("%p\n", arr);
return 0;
}
运行后发现两者结果相同,都是相同的地址。
但是在下面这一串代码中,好像情况有所不同:
#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;
}
这里输出的结果为数组的长度:40 字节
但是 arr 难道不是数组首元素的地址吗,地址的长度怎么会是 40 个字节呢?
其实,这就是两个例外中的一个。
例外情况
- sizeof(数组名):sizeof 中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小
- & 数组名:这里的数组名表示整个数组,取出的是整个数组的地址 (整个数组的地址和数组首元素的地址值一样但是类型不同 ,解引用得到的不是首元素而是整个数组)
为了更深入的理解取地址数组名与数组首元素地址的区别,我们看看这段代码:
#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 = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
其运行结果如下:
这里我们发现 & arr[0] 和 & arr[0]+1 相差 4 个字节,arr 和 arr+1 相差 4 个字节,是因为 & arr[0] 和 arr 都是
首元素的地址,+1 就是跳过一个元素;但是 & arr 和 &arr+1 相差 40 个字节,这就是因为 & arr 是数组的地址,+1 操作是跳过整个数组的。
使用指针访问数组
知道数组名的含义后,我们其实就可以通过指针来访问数组了
#include <stdio.h>
int main()
{
int arr[10] = {0};
int i = 0;
int len = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
//输入
for(i = 0; i < len; i++)
scanf("%d", p + i); //@@1
//输出
for(i = 0; i < len; i++)
printf("%d ", *(p + i)); //@@2
return 0;
}
注:上方 @@1 和 @@2 两处的 p 均可替换为 arr
所以说 arr 与 p 在这里是等价的
那我们能通过 arr[i] 来访问数组的元素,那是不是也可以通过 p[i] 来访问呢?
答案是肯定的,如下:
#include <stdio.h>
int main()
{
int arr[10] = {0};
int i = 0;
int len = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
//输入
for(i = 0; i < len; i++)
scanf("%d", p + i);
//输出
for(i = 0; i < len; i++)
printf("%d ", p[i]); //@@@
return 0;
}
所以 p[i] 实际上就等价于 *(p+i)
那么 arr[i] 是不是也就等价于 *(arr+i) 呢?
是这样的。在编译器处理时,arr[i] 实际上就是被转换成首元素指针加偏移量,然后解引用来访问的,而知道这些后,我们甚至可以玩一点新花样。
数组访问的特殊写法
我们知道加法操作符左右两边是可换的(两表达式不互相影响时)
所以 *(arr+1) 其实可以写成 *(i+arr)
而 *(arr+i) 等价于 arr[i],那么 *(i+arr) 是不是也就可以写成 i[arr] 呢?
答案是,可以!!!
所以 arr[i] 也可以写成 i[arr]!!!
不过我们一般不这么写
一维数组传参的本质
如果我们把一维数组传递给一个函数,本质上传递的是首元素的地址
这也就是为什么数组传参后函数操作会实际的影响到数组本身
因为传递的是地址,解引用后实际操作的是原参数(实参)的内存空间
而只知道首元素地址是不能确定一个数组有多长的,所以我们一般把长度一起传过去
所以接受的类型应该是指针类型:
void Print(int* arr, int len)
{
for (int i = 0; i < len; i++)
printf("%d", arr[i]);
}
但我们之前讲过数组传参可以写成这样:
void Print(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d", arr[i]);
}
实际上这两种写法等价,本质上还是指针(其实是 C 语言专门设计为可以写成这样便于理解)
这也就解释了为什么参数中 arr[] 的方括号内不需要数字,而有数字也会被忽略
Comments