[C]基础10.深入理解指针(2)
- 博客主页:向不悔
- 本篇专栏:[C]
- 您的支持,是我的创作动力。
文章目录
- 0、总结
- 1、数组名的理解
- 2、使用指针访问数组
- 3、一维数组传参的本质
- 4、冒泡排序
- 5、二级指针
- 6、指针数组
- 7、指针数组模拟二维数组
0、总结
1、数组名的理解
先思考一个问题,定义一个数组int arr[10]
,&arr[0]
、arr
、&arr
的区别是?
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%p\n", &arr[0]);
printf("%p\n", arr);
printf("%p\n", &arr);
printf("----\n");
printf("%zd\n", sizeof(arr[0]));
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(&arr));
printf("----\n");
printf("%p\n", &arr[0]+1);
printf("%p\n", arr+1);
printf("%p\n", &arr + 1);
return 0;
}
运行(32位平台):
0076FE6C
0076FE6C
0076FE6C
----
4
40
4
----
0076FE70
0076FE70
0076FE94
总结:
&arr[0]
:arr[0]
表示数组的第一个元素(int类型),&arr[0]
表示取这个元素的地址。arr
:在大多数情况下,arr
表示数组的名称,它会被隐式地转换为指向数组第一个元素的指针。&arr
:&arr
表示取整个数组的地址,而不是数组中某个元素的地址。类型为int (*)[10]
,即指向包含10个int的数组的指针。
我们再谈谈数组名,有两个例外:
- sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)
除此之外,任何地方使用数组名,数组名都表示首元素的地址。
2、使用指针访问数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
// 输入
for (int i = 0; i < sz; i++)
{
scanf("%d", p + i);
}
// 输出
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
总结:
*(arr+i) 等价于 arr[i] 等价于 *(i+arr) 等价于 i[arr]
3、一维数组传参的本质
数组我们学过了,在这里,我们思考数组传参的本质是什么?
首先,从一个问题思考,我们之前都是在函数外部计算数组的元素个数,那么我们可以把数组传给一个函数,在函数内部能求出数组的元素个数吗?如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
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 = 10
sz2 = 1
我们发现在函数内部,并没有正确获得数组的元素个数。
这时候,我们要学习数组传参的本质,我们知道,数组名是数组首元素的地址,那么在数组传参的时候,传递的是数组名,也就是说,本质上数组传参传递的是数组首元素的地址。
所以,在函数形参的部分,可以使用指针变量来接收数组首元素的地址。在函数内部我们写的sizeof(arr)
计算的是一个地址的大小(单位字节),而不是数组的大小(单位字节)。
正是因为函数的参数部分本质上是指针,所以在函数内部是没法求出数组元素个数。
总结: 一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
4、冒泡排序
冒泡排序的核心思想是:两两相邻的元素进行比较。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void bubble_sort(int* p, int sz)
{
// 共sz - 1趟,每趟解决一个数字
for (int i = 0; i < sz - 1; i++)
{
// 一趟排序的过程
for (int j = 0; j < sz - 1 - i; j++)
{
if (p[j] > p[j + 1])
{
int tmp = 0;
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 3, 1, 7, 5, 8, 9, 0, 2, 4, 6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行:
0 1 2 3 4 5 6 7 8 9
再继续优化:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void bubble_sort(int* p, int sz)
{
// 共sz - 1趟,每趟解决一个数字
for (int i = 0; i < sz - 1; i++)
{
int flag = 1; // 假设这一趟已经有序了
// 一趟排序的过程
for (int j = 0; j < sz - 1 - i; j++)
{
if (p[j] > p[j + 1])
{
flag = 0;
int tmp = 0;
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
// 这一趟没交换就说明已经有序了,后续无需排序了。
if (flag == 1)
{
break;
}
}
}
int main()
{
int arr[] = { 3, 1, 7, 5, 8, 9, 0, 2, 4, 6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行:
0 1 2 3 4 5 6 7 8 9
5、二级指针
二级指针:指针变量也是变量,是变量就有地址,而指针变量的地址存放的地方,就是二级指针。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a; // pa是一级指针变量
int** ppa = &pa; // ppa是二级指针变量
return 0;
}
注意:二级指针和二维数组没有对应关系
6、指针数组
先思考一个问题,指针数组是指针还是数组?
可以通过类比:整型数组,是存放整型的数组,字符数组,是存放字符的数组。
因此,指针数组:存放指针的数组。
指针数组的每个元素都是用来存放地址(指针)的。如图:
7、指针数组模拟二维数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
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[3] = { arr1, arr2, arr3 };
int i = 0;
int j = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
parr[i]
是访问parr数组的元素,parr[i]
找到的数组元素指向了整型一维数组,parr[i][j]
就是整型一维数组中的元素。
上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的。
完。