深入解析:【C语言】13.指针(3)

在这里插入图片描述

个人主页: 缘三水的博客
专栏传送门:C语言专栏(新手向)
人生格言:行动是迷茫的最好解药


个人介绍:
在这里插入图片描述


往篇回顾

【C语言】指针(1)
【C语言】指针(2)



正文开始

(一)数组名的理解

数组名是首元素的地址

示例

int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] : %p\n", &arr[0]);
printf("arr     : %p\n", arr);
return 0;
}

结果
在这里插入图片描述

拓展: %p 是专门用来打印地址的占位符,以16进制形式打印

但是有两个特殊情况

特殊情况1
sizeof(数组名)

这里的数组名表示整个数组
计算数组名的数据类型长度时不是4或者8
而是 (元素个数 X 每一个元素的数据类型长度)

示例
在这里插入图片描述

特殊情况2
&数组名

&数组名,在值上与首元素的地址相同,但实际含义不同
&数组名,实际上是取出整个数组的地址

两者的指针类型是不同的
数组名(首元素地址)±整数,跨度是一个元素的大小
&数组名±整数,跨度是一个数组的大小

示例
在这里插入图片描述
&arr[0]和&arr[0]+1相差4个字节
arr和arr+1相差4个字节
+1就是跳过⼀个元素

然而&arr和&arr+1相差40个字节
+1操作是跳过整个数组

(二)指针访问数组

问题 向数组输入值,并打印

示例1

int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)//输入
{
scanf("%d",&arr[i]);
}
for (int i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
return 0;
}

示例2利用指针向数组里输入值,改变数组内容

int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i < sz; i++)
{
scanf("%d", p + i);//p可以改为arr
}
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p+i));//p可以改为arr
}
return 0;
}

也可以将输入和打印中的p改为arr
说明数组名就是首元素的地址,arr和p在这里是等价的

我们再看示例1和示例2中的printf函数,得出

arr[ i ] ==  * (arr + i)

数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移
求出元素的地址,然后解引用来访问的

又因为

*(arr + i) ==  *(i + arr)

而且

*(i+arr)== i[arr]//这种形式不常写,影响理解代码的效率

因此1,我们得出一种类似加法交换律的结论

arr[ i ] == * (arr + i) = = *(i +arr) == i[arr]

(三)一维数组传参本质

在这里插入图片描述
由结果看见
两个计算数组元素个数的结果不相同

实际上是因为一维数组传参,传的数组名是首元素的地址
只有在上面提到的两种特殊情况中,数组名才代表整个数组

这里函数的参数部分是本质是指针
因此,传一维数组应该用指针变量接收

而我们这里将首元素地址用int类型接收
指针变量计算大小则由环境决定
在x64环境下,指针变量大小是8,因此得出sz2 = 2

但是如果我们写int*arr接收的话,则程序会出现问题
总结:
一维数组传参本质上传的是首元素的地址
在函数内部求一维数组的元素个数是错误的

⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式

示例
用int*接收,打印数组内容

在这里插入图片描述

(四)冒泡排序

冒泡排序两两相邻的元素进行比较

问题
一组数字,有n个元素,要排成升序形式

1.确定趟数
n个元素–进行n-1趟排序
2.一趟内部两两比较
一趟排序,数字内部两两相邻进行比较
前面数字大,就进行两个值的交换
直到成为正确的排序
在这里插入图片描述
根据上面两个步骤,写出代码

//冒泡排序
//排成升序形式,两两相邻元素进行比较
void bubble_sort(int arr[], int sz)
{
//1.确定趟数
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//2.一趟内部排序
int j = 0;
for (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;
}
}
}
}
void print_num(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
print_num(arr, sz);
return 0;
}

但是如果碰见在n-1趟之内就已经排序好的,仍然需要进行比较,这就影响了效率

于是我们可以这样优化
在进行一次无需交换的趟数时,说明已经排序好了,就直接break跳出循环,排序结束
而我们可以用一个状态量来判断是否进行了一次无需交换的趟

部分代码优化

//优化
void bubble_sort(int arr[], int sz)
{
//1.确定趟数
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//2.一趟内部排序
int j = 0;
//假定排序好
int flag = 1;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])//进行一次无需交换的趟,说明不进入if语句内部,flag值仍为1
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;//说明是经过一趟排序,不是排序好的,flag赋为0
}
}
if (flag == 1)
break;//经过一次无需交换趟数,说明已经排序完,无需再判断,跳出循环
}
}

(五)二级指针

我们知道指针变量也是变量,那指针变量是不是也有自己对应的地址(指针)呢?
是的,一级指针变量对应的地址被称作二级指针

int a = 10int* pa = &a;//一级指针
int** ppa = &pa;//二级指针

我们来拆解这个二级指针
可以看到二级指针有两个*
后面的*说明这个ppa是指针变量
前面的 int *则说明这个指针指向的类型是 int *类型的

使用

**ppa  等价于 *pa  等价于  变量a

(六)指针数组

定义
指针数组是存放指针的数组

类型
因为数组的类型是由元素的类型决定
所以,如果存放的是整型类型的元素的地址,那么数组类型就是int *
如果存放的是字符类型的元素的地址,那么数组类型就是 char *

示例

int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = { &a,&b,&c };
return 0;
}

使用示例
利用指针数组,模拟一个二维数组

int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* arr[] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *(arr[i] + j));//*(arr[i] + j) == arr[i][j]
}
printf("\n");
}
return 0;
}

打印每一个元素时,也可以这样写
*(arr[ i ] + j) == arr[ i ][ j ]

在这里插入图片描述


总结

这篇文章我们详细介绍了指针部分内容,希望能对你有帮助

有错误的地方希望可以得到大佬的指正
最后不要吝啬你的点赞,收藏和评论!!!
感谢你的观看

posted @ 2026-01-11 21:30  yangykaifa  阅读(6)  评论(0)    收藏  举报