1.问题引出
案例:设计一个函数,可以将整形数组的次序调换
例如:arr[5] = {1,2,3,4,5},输出形式为:arr[5] = {5,4,3,2,1}.
案例代码:
//能否可以正常排序?
#include <stdio.h>
void reverse(int* arr)
{
int len = sizeof(arr) / sizeof(arr[0]);
int top = 0;
int tail = len - 1;
while (top < tail)
{
int tmp = 0;
tmp = arr[top];
arr[top] = arr[tail];
arr[tail] = tmp;
top++;
tail--;
}
}
int main()
{
int arr[] = { 1,2,3,4,5 };
int len = sizeof(arr) / sizeof(arr[0]);
reverse(arr);
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
我们来运行一下:
我们发现结果并不是和我们想象的一样输出 5 4 3 2 1 ,而是对于原数组来说没有变化,这是为什么呢?
2.分析错误代码
首先,按F10,点击调试——>窗口——>监视
在监视窗口中输入arr
,这时是运行在主函数中,所以监控的是主函数中的arr
这里能够看到,arr
中储存着5值
然后按F11进入函数内部,这时的监视窗口是这样:
这里有个技巧:需要在监视中输入arr,5
才能正常监视到:
代码再往下走,我们发现len
的值为1
但是int len = sizeof(arr) / sizeof(arr[0])
,len
的值应该为arr
数组的长度啊,应该是5,为什么是1呢,这就需要了解数组名到底是什么
3.数组名是什么?
先探究数组名与数组元素首元素地址:
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr); //数组名
printf("%p\n", &arr[0]);//数组首元素地址
return 0;
}
结果:
所以得出结论:数组名是数组首元素的地址
但是有两个例外:
1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
2.&数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址
验证一下sizeof(数组名):
int main()
{
int arr[10] = { 0 };
printf("%d", sizeof(arr));
return 0;
}
结果:
数组长度为10,int
类型一个元素4个字节,所以输出40,这就说明:
sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小
验证 &数组名:
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr); //数组名
printf("%p\n", &arr[0]);//数组首元素地址
printf("%p\n", &arr); //数组名取地址
return 0;
}
结果:
发现这三个输出的地址都相同,它们有什么区别呢?我们再来探究一下
arr
与·&arr[0]
一样,都是指向数组首元素地址,他们的类型都是int*
而&arr
是整个数组的地址,&arr
的类型为:int(*)[10]
由于这个数组的地址由数组首元素地址开头,所以这三个地址值从表面上看起来一样,但可以从他们的指针类型去区别他们
再对比一下:
int main()
{
int arr[10] = { 0 };
printf("arr: %p\n", arr);
printf("arr+1: %p\n", arr+1);
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);
return 0;
}
结果:
分析:
arr
与&arr[0]
都跳四个字节,也就是数组下一个元素地址
而&arr
跳过了40个字节,它跳过了整个数组
从这里的计算,也可以体会出arr
与&arr[0]
的类型是int*
,而&arr
的类型是int(*)[10]
4.错误分析与修改
错误分析:
前面的内容我们知道了数组名是数组首元素地址,本质上是个指针
所以在函数中,就需要用int*
去接收这个指针变量
void reverse(int* arr)
{
int len = sizeof(arr) / sizeof(arr[0]);
int top = 0;
int tail = len - 1;
while (top < tail)
{
int tmp = 0;
tmp = arr[top];
arr[top] = arr[tail];
arr[tail] = tmp;
top++;
tail--;
}
}
而在
reverse
内部,sizeof(arr)
中的arr
被判定成了指针,而不是一个数组,所以sizeof(arr)
的值为4,sizeof(arr[0])
也为4,所以len
的值为1,而top=0
,tail = len-1 = 0
,top = tail
,根本进不去下面的循环,所以数组才不会改变
修改:
因为数组作为参数,它的长度不可以在函数内部被计算出来,所以就要在主函数中把数组的长度计算出来,将数组长度作为参数传到函数中
#include <stdio.h>
void reverse(int* arr,int len)
{
int top = 0;
int tail = len - 1;
while (top < tail)
{
int tmp = 0;
tmp = arr[top];
arr[top] = arr[tail];
arr[tail] = tmp;
top++;
tail--;
}
}
int main()
{
int arr[] = { 1,2,3,4,5 };
int len = sizeof(arr) / sizeof(arr[0]);
reverse(arr,len); //两个参数,一个是数组首地址,一个是数组长度
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
结果正确:
因为我们知道数组名其实就是一个指针,所以在函数的参数列表中,可以使用
int arr[]
去接受,也可以用int* arr
去接受收
//这两种形式都可以
void reverse(int* arr,int len);
void reverse(int arr[],int len);
5.总结
- 1.一般情况下,数组名就是数组首元素的地址
- 2.有两个特殊情况:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址。
- 3.有数组作为参数传入的函数中,想要计算这个数组的长度,在函数内部做不到,需要在主函数中将数组长度计算出来,将数组长度作为参数传入函数中,这才可以在函数中使用