一、数组名的理解
我们直接使用%p打印出地址来看看&arr【0】 和 arr的不同:
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("%d\n", sizeof(arr));
那上面这个代码的打印结果是什么呢?
——40
但是我们刚刚知道数组名表示的数组的首元素地址,打印的结果不应该是4或者8吗?
是刚刚的结论错了吗?
当然不是,只不过是有两个例外:
*sizeof(数组名)
在sizeof中单独的放一个数组名,这个数组名就表示整个数组,计算的也就是整个数组的大小 4*10就是40了,单位是字节。
*&数组名
这里的数组名也是表示整个数组,取出的是整个数组的地址。
注意:整个数组的地址和数组首元素地址是有区别的!
举个简单例子:&arr + 1 就是跳过一整个数组;arr + 1 则是跳过数组中的一个元素。
用下面的代码直观感受一下吧
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;
}
(在十六进制中 c 就是12) (48 ~ 70在十六进制中就是3*16+2=40)
通过运行代码我们可以看到,这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是⾸元素的地址,+1就是跳过⼀个元素。但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。
数组名是数组⾸元素的地址,但是有2个例外(sizeof(数组名)和 &数组名)
二、用指针访问数组
还是用一组代码来讲解
int main() {
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (i = 0; i < sz; i++) {
scanf("%d", p + i);
}
for (i = 0; i < sz; i++) {
printf("%-2d", *(p + i));
}
return 0;
}
因为数组名arr是数组首元素的地址,将其赋值给指针p,这样p就等价与arr,p也指向了数组首元素的地址,这时候使用*(p + i)和p[i]都是可以访问数组的
因为*(p + i)是等价于 p[i]
数组传参的本质
我们在上一节知道了,传参是有传值和传址两种方式的
就可以通过将求数组元素个数的步骤放在函数中来实现
代码如下:
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的值并不一样!
如果是传数值的话,两个sz计算出来的结果应该是一样的,结果不一样说明传的是地址,在函数test中的arr是一个存放地址的指针,所以sizeof(arr)计算的是一个地址的字符大小,这样的结果当然是不一样的。
我们可以在定义函数形参的时候就用指针定义
将int arr【】 换成 int * arr更为直观,可以减少出错!
一维数组传参,形参部分可以写成数组的形式也可以写成指针形式。