文章目录
前言
提示:这里可以添加本文要记录的大概内容:
本文主要写了指针与一维数组的关系、二级指针的引入、指针数组的介绍。
提示:以下是本篇文章正文内容,下面案例可供参考
一、指针与一维数组
(一)、数组名的理解
1.数组名使用的两种特殊情况
- sizeof(数组名)以及&数组名,在这两种情况中,数组名代表的是整个数组。sizeof(数组名),计算的是整个数组的大小,单位是字节;&数组名,取出的是整个数组的地址(虽然整个数组的地址与数组首元素的地址指向的是同一个位置,但是它们是有区别的)
- 除了以上两种情况外,在任何地方使用数组名,数组名都表示首元素的地址
2.整个数组的地址和数组首元素地址的区别
- 代码如下:
#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 sz = sizeof(arr)/sizeof(arr[0]);
int* p = arr;
for(i=0; i<sz; i++)
{
scanf("%d", p+i);
//也可以写成scanf("%d",arr+i)
}
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));
}
return 0;
- 既然数组名arr是数组首元素的地址,可以赋值给p,其实数名arr和p在这里是等价的。那我们可以使用arr[i]可以访问数组的元素,那p[i]同样也可以访问数组。在上面的代码中将*(p+i)换成p[i]也是能够正常打印的,所以本质上来说p[i]是等价于*(p+i)的;同理arr[i]应该等价于*(arr+i)
(三)、一维数组传参的本质
- 本质上说数组传参传递的是首元素的地址,这里我们举一个典型的例子来说明传参传的是数组首元素的地址:
我们通过在函数外面求数组的元素个数和在函数内部求数组元素个数的差异来说明 - 代码层面解释:
#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;
}
运行结果很明显:
在函数外面,数组名在sizeof中代表的是整个数组,所以最终能求出整个数组正确的元素个数10个;而在函数内部sizeof中的数组名代表的是数组首元素的地址,故而sizeof(arr)计算的是一个地址的大小(单位是字节)而不是数组的大小(单位字节)。这个例子侧面反映出来函数的参数部分本质是指针(地址),所以在函数内部无法求得正确的数组元素的个数
- 一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式
#include<stdio.h>
void test(int arr[])
{
printf("%d\n", sizeof(arr));
}
void test(int*arr)
{
printf("%d\n", sizeof(arr));
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
test(arr);
return 0;
}
通过以上代码我们可以得到:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式,这两者是可以相互转换的
(四)、实现冒泡排序
- 关键在于确定好趟数,核心思想就是两两相邻的元素进行比较,大的元素放后面,小的元素放前面,由上面总结的结论可以知道一维数组传参,形参部分可以写成数组形式,也可以写成指针形式
代码实现:
# include<stdio.h>
void input(int* arr, int sz)//输入排序内容
//这里我们以指针形式接受,下面操作的时候我们也可以写成数组形式,它们两个可以相互转换
{
for (int i = 0; i < sz; i++)
{
scanf("%d", arr + i);
}
}
void bupple_sort(int* arr, int sz)//实现冒泡排序算法
//这里我们以指针形式接受,下面操作的时候我们也可以写成数组形式,它们两个可以相互转换
{
for (int i = 0; i < sz-1; i++)//sz-1趟比较
{
int change = 1;//小优化节省时间
for (int j = 0; j < sz-1 - i; j++)//每一趟要比较的次数
{
if (arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
change = 0;
}
}
if (change == 1)//说明已经有序
{
break;
}
}
}
void print(int* arr, int sz)//打印排序后的内容
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
printf("\n");
}
int main()
{
int arr[10] = { 0};
int sz = sizeof(arr) / sizeof(arr[0]);//获得数组中元素个数
input(arr, sz);
bupple_sort(arr, sz);
print(arr, sz);
return 0;
}
- 这里我们引入change变量来做算法小优化,一开始给change赋值为1,如果经过一趟的比较,change的值没有发生改变,说明后面的序列已经有序了,直接退出循环即可。
二、二级指针
(一)、对二级指针概念的理解
- 我们知道指针变量也是变量,有变量就会有地址,而指针变量的地址就是二级指针。
# include<stdio.h>
int main()
{
int a=10;
int *pa=&a;
int **ppa = &pa;
return 0;
}
- 这里的ppa就是二级指针,它表示存储的是pa一级指针的地址。
(二)、二级指针的运算
- ppa是我们定义的二级指针,*ppa就是通过ppa中的地址进行解引用,这样我们找到的是一级指针变量pa
int b=20;
*ppa=&b;//这里就等价于pa=&b;
- **ppa双重解引用,我们先通过*ppa找到pa;然后对pa进行解引用,*pa找到的是a变量。
**ppa=30;
//等价于*pa=30、a=30;
三、指针数组
(一)、指针数组相关知识点
- 首先进行类比工作:
整型数组存放的是整型的数组;字符数组存放的是字符的数组,同理那么我们要介绍的指针数组就是存放指针的数组
如图解释其构造。
(二)、指针数组模拟二维数组
- 代码如下:
#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};
//这里的arr1.arr2,arr3为数组名代表的是数组首元素的地址,类型是int*,可以放到prr数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
- 此过程的画图解释:
- parr[i]是访问parr数组中的元素,parr[i]找到的是数组元素指向了整型的一维数组,parr[i][j]就是整型一维数组中的元素。
- 注意上面只是模拟出了二维数组的效果,但是不是真正的二维数组,因为每一行并非是连续的。
总结
本文对指针进行了进一步的解读,介绍了指针与一维数组的关系、二级指针的引入以及指针数组的相关概念与应用。
如有错误,请批评指正,感谢支持。