当前位置: 首页 > news >正文

第13讲:深入理解指针(3)——数组与指针的“深度绑定”

🧭 第13讲:深入理解指针(3)——数组与指针的“深度绑定” 🔗📊

指针进阶:揭开数组名的神秘面纱,掌握传参本质与指针数组魔法!


📚 目录

  1. 数组名的理解:是地址,但不止是地址 🧩
  2. 使用指针访问数组:指针即数组,数组即指针 ✨
  3. 一维数组传参的本质:形参写数组,实为指针 🔁
  4. 冒泡排序:指针视角下的“相邻较量” 🔄
  5. 二级指针:指针的“地址管理员” 🏢
  6. 指针数组:存放指针的“容器” 🧰
  7. 指针数组模拟二维数组:非连续的“矩阵” 📐
  8. 学习收获总结 ✅

数组名的理解:是地址,但不止是地址 🧩

🔍 数组名 = 首元素地址?

在指针访问数组时,我们常写:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0]; // 获取首元素地址

但其实,数组名本身就是首元素的地址

#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    = %p\n", arr);      // 输出相同地址return 0;
}

输出结果一致 → 证明 arr == &arr[0]


⚠️ 矛盾出现:sizeof(arr) 为何是 40?

printf("%d\n", sizeof(arr)); // 输出 40(int占4字节 × 10)

如果 arr 是地址,应输出 4 或 8(指针大小),为何是 40?

🎯 关键突破
数组名是首元素地址
但有两个例外!


📌 两大例外:sizeof& 中的数组名

场景数组名含义说明
sizeof(arr)表示整个数组计算数组总大小(字节)
&arr表示整个数组的地址类型为 int(*)[10],指向整个数组

🌟 重要区别
&arr&arr[0] 虽值相同,但类型不同,导致指针运算结果不同!

printf("&arr[0]   = %p\n", &arr[0]);     // 首元素地址
printf("&arr[0]+1 = %p\n", &arr[0]+1);   // +1 → 跳过1个int(+4字节)
printf("arr       = %p\n", arr);         // 首元素地址
printf("arr+1     = %p\n", arr+1);       // +1 → 跳过1个int(+4字节)
printf("&arr      = %p\n", &arr);        // 整个数组地址
printf("&arr+1    = %p\n", &arr+1);      // +1 → 跳过整个数组(+40字节)

📌 结论

  • &arr[0]arr:类型为 int*,+1 跳 4 字节
  • &arr:类型为 int(*)[10],+1 跳 40 字节(整个数组)

记忆口诀
“普通使用是首址,sizeof& 是整体”


使用指针访问数组:指针即数组,数组即指针 ✨

🔄 指针遍历数组

#include <stdio.h>
int main() {int arr[10] = {0};int sz = sizeof(arr)/sizeof(arr[0]);int *p = arr; // p 指向首元素// 输入for(int i = 0; i < sz; i++) {scanf("%d", p + i); // 等价于 &arr[i]}// 输出for(int i = 0; i < sz; i++) {printf("%d ", *(p + i)); // 等价于 arr[i]}return 0;
}

📌 p + i:指针偏移 i 个元素,指向 arr[i]


🎯 p[i] 真的能访问数组吗?

// 将 *(p+i) 改为 p[i]
printf("%d ", p[i]); // ✅ 完全合法!

🌟 核心原理

  • p[i] 在编译器中被解释为 *(p + i)
  • 同理,arr[i] 等价于 *(arr + i)

结论
数组下标访问本质上是指针运算
arr[i]p[i] 在语法上完全等价。


一维数组传参的本质:形参写数组,实为指针 🔁

❓ 问题:函数内能否求出数组长度?

void test(int arr[]) {int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2); // ❌ 输出 1 或 2(指针大小 / int大小)
}int main() {int arr[10] = {1,2,3,4,5,6,7,8,9,10};int sz1 = sizeof(arr)/sizeof(arr[0]); // ✅ 输出 10test(arr);return 0;
}

🤔 为何 sz2 不是 10?


🔍 数组传参的本质

  • 传递的是数组名 → 即首元素地址
  • 函数形参接收的是指针,不是整个数组
void test(int arr[])  // 等价于 int* arr
{printf("%d\n", sizeof(arr)); // 输出指针大小(4或8)
}void test(int* arr)   // 完全等价
{printf("%d\n", sizeof(arr)); // 输出指针大小
}

📌 本质
一维数组传参时,形参无论写成 int arr[] 还是 int* arr,都退化为指针


✅ 正确传参方式

void func(int* arr, int sz) {// 使用 arr 和 sz 操作数组for(int i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}

🎯 总结
一维数组传参,必须额外传递数组长度


冒泡排序:指针视角下的“相邻较量” 🔄

🔄 基础版本

void bubble_sort(int arr[], int sz) {for(int i = 0; i < sz - 1; i++) {for(int j = 0; j < sz - i - 1; j++) {if(arr[j] > arr[j+1]) {int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}

📌 核心思想:两两相邻元素比较,大者后移。


🚀 优化版本:提前终止

void bubble_sort(int arr[], int sz) {for(int i = 0; i < sz - 1; i++) {int flag = 1; // 假设已有序for(int j = 0; j < sz - i - 1; j++) {if(arr[j] > arr[j+1]) {flag = 0; // 发生交换,说明无序int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}if(flag) break; // 未发生交换,提前结束}
}

优势:已有序时提前退出,提升效率。


二级指针:指针的“地址管理员” 🏢

🧩 什么是二级指针?

指针变量也有地址,存放指针变量地址的指针,就是二级指针

int a = 10;
int *pa = &a;      // pa 存放 a 的地址
int **ppa = &pa;   // ppa 存放 pa 的地址

📌 类型:

  • pa 类型:int*
  • ppa 类型:int**

🔄 二级指针的运算

表达式含义
*ppa解引用 → 得到 pa(等价于 pa
**ppa两次解引用 → 得到 a 的值
**ppa = 20;     // 等价于 a = 20
*ppa = &b;      // 等价于 pa = &b

🎯 应用场景:修改指针本身(如动态内存分配、链表操作)。


指针数组:存放指针的“容器” 🧰

🧩 指针数组 vs 整型数组

数组类型存放内容示例
整型数组 int arr[3]整数{1, 2, 3}
指针数组 int* parr[3]指针(地址){&a, &b, &c}
int a = 1, b = 2, c = 3;
int* parr[3] = {&a, &b, &c}; // 每个元素是一个 int* 指针

📌 本质
指针数组是一个数组,其每个元素都是指针类型


指针数组模拟二维数组:非连续的“矩阵” 📐

🔄 模拟代码

#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}; // 指针数组,指向三个一维数组for(int i = 0; i < 3; i++) {for(int j = 0; j < 5; j++) {printf("%d ", parr[i][j]); // ✅ 可以像二维数组一样访问}printf("\n");}return 0;
}

输出

1 2 3 4 5
2 3 4 5 6
3 4 5 6 7

⚠️ 与真正二维数组的区别

特性真正二维数组 int arr[3][5]指针数组模拟
内存布局连续存储每行可能不连续
初始化静态分配需先定义每行数组
灵活性固定大小可动态调整每行大小

📌 优势
指针数组可实现不规则二维数组(如每行长度不同)。


✅ 学习收获总结

技能掌握情况
✅ 理解数组名的本质及两大例外✔️
✅ 掌握指针与数组的等价访问方式✔️
✅ 理解一维数组传参的指针本质✔️
✅ 能实现并优化冒泡排序算法✔️
✅ 理解二级指针的概念与应用✔️
✅ 区分指针数组与数组指针✔️
✅ 会使用指针数组模拟二维数组✔️

🎯 数组与指针的深度绑定,是C语言高效与灵活的基石
你已掌握其核心逻辑,继续前行,迈向指针的更高境界!💪🔥

💬 需要本讲的 指针数组练习题 / 冒泡排序动画解析 / 二级指针实战案例?欢迎继续提问,我为你准备!

http://www.dtcms.com/a/478516.html

相关文章:

  • 基于MATLAB的匈牙利算法实现任务分配
  • Type-C 接口充电兼容设计(针对 5V1A 需求)
  • Anaconda 学习手册记录
  • Python-适用于硬件测试的小工具
  • 第三方软件测评机构:【Locust的性能测试和负载测试】
  • 【Python】列表 元组 字典 文件
  • 简单asp网站深圳做个商城网站设计
  • OpenTelemetry 入门
  • 昆山做网站找哪家好wordpress 算数验证码
  • 网站建设服务费入阿里云域名注册平台
  • 美颜的灵魂:磨皮技术的演进与实现原理详解
  • 自定义半精度浮点数modelsim仿真显示
  • 广东GEO优化哪家专业哪家服务好
  • 【C#】await Task.Delay(100)与Thread.Sleep(100)?
  • 从智能补全到云原生适配:免费IDE DataGrip的技术实践与行业趋势
  • 多摄像头网络压力测试
  • 信息发布网站设计巴中网站建设有限公司
  • 图像处理-opencv(一)
  • 空包网站分站怎么做重庆市工程建筑造价信息网
  • 基于MATLAB的Excel文件批量读取与循环处理
  • 网站建设方案拓扑图平面设计现在怎么样
  • 高并发下的优雅延迟:Python异步爬虫(aiohttp)的速率限制实践
  • Python爬虫实战:获取同花顺股票资金流向数据并做分析
  • MyBatis的SpringBootVFS解析
  • 上海网站建设好处自建网站教程
  • 网站建设的作用是什么网站建设沟通准备
  • 【中间件Linux在环境安装】SpringBoot应用环境安装(一)-JDK安装
  • 【Linux环境下安装】SpringBoot应用环境安装(二)-Redis安装
  • 如何设置 Visual Studio 在调试停止时自动关闭控制台
  • 网站建设佰首选金手指三十wordpress 切换中文字体