【C语言】深入理解指针(二):从数组到二维数组的指针魔法

前言

在C语言中,指针一直是一个神秘而强大的存在。它不仅可以帮助我们高效地操作内存,还能让代码更加灵活和高效。今天,我们就来深入探讨指针的多种用法,从数组到二维数组,一步步揭开指针的神秘面纱。

一、数组名的指针本质

数组名到底是什么?很多初学者可能会认为数组名只是一个简单的标识符,但实际上,数组名在大多数情况下表示的是数组首元素的地址。我们来看一个简单的例子:

#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[0] 和 arr 打印出来的地址是一样的。这说明数组名 arr 本质上就是数组首元素的地址。
但是,这里有两个例外情况:
sizeof(数组名):此时数组名表示整个数组,计算的是整个数组的大小,单位是字节。
&数组名:此时数组名表示整个数组,取出的是整个数组的地址,而不是首元素的地址。
我们再来看两个代码示例来验证:

#include <stdio.h>
int main()
{int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};printf("sizeof(arr) = %zd\n", sizeof(arr)); // 输出整个数组的大小printf("&arr = %p\n", &arr); // 输出整个数组的地址return 0;
}

在这里插入图片描述

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);printf("&arr    = %p\n", &arr);return 0;
}

在这里插入图片描述

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;
}

在这里插入图片描述

通过例子,我们可以清楚地看到数组名的两种特殊情况。

二、使用指针访问数组

既然数组名是首元素的地址,那么我们就可以通过指针来访问数组中的元素。来看一个代码示例:

#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); // 使用指针访问数组元素}for (i = 0; i < sz; i++){printf("%d ", *(p + i)); // 使用指针访问数组元素}return 0;
}

在这里插入图片描述

在这个例子中,我们通过指针 p 来访问数组 arr 的元素。p + i 表示的是数组中第 i 个元素的地址,而 *(p + i) 则表示的是第 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;
}

在这里插入图片描述

运行结果会发现,在函数内部,sz2 的值并不是数组的实际大小,而是 1。这是因为数组传参的本质是传递数组首元素的地址,而不是整个数组。在函数内部,arr 是一个指针变量,sizeof(arr) 计算的是指针变量的大小,而不是数组的大小。
所以,一维数组传参的本质是传递数组首元素的地址。函数的形参部分可以写成数组的形式,也可以写成指针的形式,但本质上都是指针。

四、冒泡排序的实现

冒泡排序是一种简单的排序算法,它的核心思想是两两相邻的元素进行比较。我们来看一个冒泡排序的实现代码:

#include <stdio.h>
void bubble_sort(int arr[], int sz)
{int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for (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;}}}
}
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;
}

在这里插入图片描述

优化代码:

void bubble_sort(int arr[], int sz)
{int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;int flag = 1;for (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 == 1){break;}}
}int main()
{int arr[10] = { 3,1,7,5,8,9,0,2,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述

这个代码实现了冒泡排序的基本逻辑。它通过两层循环,逐个比较相邻的元素,并在必要时交换它们的位置,最终实现数组的排序。

五、二级指针的奥秘

二级指针是一个指向指针的指针。它可以帮助我们更灵活地操作指针。我们来看一个二级指针的代码示例:

#include <stdio.h>
int main()
{int a = 10;int* pa = &a;int** ppa = &pa;printf("a = %d\n", a);printf("pa = %p\n", pa);printf("ppa = %p\n", ppa);printf("*pa = %d\n", *pa);printf("**ppa = %d\n", **ppa);return 0;
}

在这里插入图片描述

在这个例子中,pa 是一个指向 a 的指针,而 ppa 是一个指向 pa 的指针。通过二级指针,我们可以更灵活地操作指针变量。

六、指针数组的魔法

指针数组是一个数组,它的每个元素都是一个指针。我们可以用指针数组来模拟二维数组。来看一个代码示例:

#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 (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);}printf("\n");}return 0;
}

在这里插入图片描述
在这里插入图片描述

在这个例子中,parr 是一个指针数组,它的每个元素都是一个指向整型数组的指针。通过指针数组,我们可以模拟出二维数组的效果,但需要注意的是,每一行并不是连续的。

七、总结

通过今天的探讨,我们深入理解了指针的多种用法,从数组到二维数组,指针都扮演了重要的角色。数组名本质上是首元素的地址,通过指针可以高效地访问数组元素。一维数组传参的本质是传递首元素的地址,而二级指针和指针数组则为我们提供了更灵活的操作方式。

希望这篇文章能帮助你更好地理解指针的奥秘。如果你对指针还有其他疑问,欢迎在评论区留言,我们一起探讨!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38202.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】事务

目录 基本概念事务操作自动提交事务开启事务提交事务回滚事务代码示例 事务的特性 ACID事务的隔离级别读未提交 read uncommitted读已提交 read committed可重复读 repeatable read序列化&#xff08;串行&#xff09; serializable操作示例 基本概念 在 MySQL 中的事务&#…

flutter doctor提示cmdline-tools component is missing错误的解决

flutter doctor检测环境后报错如下: STEP1: 配置command-lines &#x1f4cc; 打开Androidstudio &#xff0c;找到sdkmanager &#x1f447; 安装command-line tools 如果找不到&#xff0c;记得打开右下角的「Show Package Details} 再次运行flutter doctor 即可正常 如…

iptables和netfilter内部报文处理

一、Iptables和netfilter 1.iptables基础 netfilter强大功能以及灵活性是通过iptables界面来实现。此命令行工具和它的前身ipchains语法相似&#xff1b;不过iptables使用netfilter子系统来增进网络连接、检验和处理方面的能力&#xff1b;ipchains使用错综复杂的规则集合来过…

[项目]基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050配置与读取

基于FreeRTOS的STM32四轴飞行器: 十一.MPU6050 一.芯片介绍二.配置I2C三.编写驱动四.读取任务的测试 一.芯片介绍 芯片应该放置在PCB中间&#xff0c;X Y轴原点&#xff0c;敏感度131表示范围越小越灵敏。理想状态放置在地面上X&#xff0c;Y&#xff0c;Z轴为0&#xff0c;即…

JVM垃圾回收笔记01

文章目录 前言1. 如何判断对象可以回收1.1 引用计数法1.2 可达性分析算法查看根对象哪些对象可以作为 GC Root ?对象可以被回收&#xff0c;就代表一定会被回收吗&#xff1f; 1.3 引用类型1.强引用&#xff08;StrongReference&#xff09;2.软引用&#xff08;SoftReference…

解决Popwindow宽高的问题。

问题 在使用Popwindow进行自定义的过程中&#xff0c;需要设置popwindow的宽高。但是宽高很多时候容易出问题。比如下面的例子。 布局文件如下 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andr…

Bell-1量子计算机分析:开启量子计算2.0时代的创新引擎

Bell-1量子计算机:开启量子计算2.0时代的创新引擎 一、引言 1.1 研究背景 在当今科技飞速发展的时代,量子计算作为前沿领域,正深刻地改变着科技格局,引领新一轮科技革命与产业变革。自 20 世纪 80 年代量子计算概念被提出以来,历经多年的理论探索与技术攻坚,已取得了众…

什么?中断禁用失效了?

什么&#xff1f;中断禁用失效了&#xff1f; 1. 前言 道友们&#xff0c;在嵌入式的开发中我们不管是RTOS或NO-RTOS的开发&#xff0c;都无法避免“多线程”的应用场景&#xff0c;高优先级的任务或中断打断低优先级的任务或中断&#xff0c;此时为了要保证共享数据的安全性…

单表达式倒计时工具:datetime的极度优雅(Kimi)

一个简单表达式&#xff0c;也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建&#xff0c;本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pyth…

[笔记.AI]多头自注意力机制(Multi-Head Attention)

多头自注意力是深度学习领域&#xff0c;特别是自然语言处理&#xff08;NLP&#xff09;和Transformer模型中的关键概念。其发展源于对序列数据中复杂依赖关系的建模需求&#xff0c;特别是在Transformer架构的背景下。 举例 比喻-读长篇文章 用一个简单的比喻来理解“多头注…

SOFABoot-02-模块化隔离方案

sofaboot 前言 大家好&#xff0c;我是老马。 sofastack 其实出来很久了&#xff0c;第一次应该是在 2022 年左右开始关注&#xff0c;但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概览 SOFABoot-01-蚂蚁金…

【实用部署教程】olmOCR智能PDF文本提取系统:从安装到可视化界面实现

文章目录 引言系统要求1. 环境准备&#xff1a;安装Miniconda激活环境 2. 配置pip源加速下载3. 配置学术加速&#xff08;访问国外资源&#xff09;4. 安装系统依赖5. 安装OLMOCR6. 运行OLMOCR处理PDF文档7. 理解OLMOCR输出结果9. 可视化UI界面9.1 安装界面依赖9.2 创建界面应用…

asp.net core mvc模块化开发

razor类库 新建PluginController using Microsoft.AspNetCore.Mvc;namespace RazorClassLibrary1.Controllers {public class PluginController : Controller{public IActionResult Index(){return View();}} }Views下Plugin下新建Index.cshtml {ViewBag.Title "插件页…

边缘计算革命:重构软件架构的范式与未来

摘要 边缘计算通过将算力下沉至网络边缘&#xff0c;正在颠覆传统中心化软件架构的设计逻辑。本文系统分析了边缘计算对软件架构的范式革新&#xff0c;包括分布式分层架构、实时资源调度、安全防护体系等技术变革&#xff0c;并结合工业物联网、智慧医疗等场景案例&#xff0c…

单链表:数据结构的灵动之链

本文主要讲解链表的概念和结构以及实现单链表 目录 一、链表的概念及结构 二、单链表的实现 1.1链表的实现&#xff1a; 1.2单链表的实现&#xff1a; 单链表尾插&#xff1a; 单链表的头插&#xff1a; 单链表的尾删&#xff1a; 单链表头删&#xff1a; 单链表查找&#…

链表题型-链表操作-JS

一定要注意链表现在的头节点是空节点还是有值的节点。 一、移除链表中的元素 有两种方式&#xff0c;直接使用原来的链表进行删除操作&#xff1b;设置一个虚拟头节点进行删除操作。 直接使用原来的链表进行删除操作时&#xff0c;需要考虑是不是头节点&#xff0c;因为移除…

读《浪潮之巅》:探寻科技产业的兴衰密码

引言&#xff1a;邂逅《浪潮之巅》 在信息技术飞速发展的今天&#xff0c;科技公司如繁星般闪烁&#xff0c;又似流星般划过。而我与《浪潮之巅》的相遇&#xff0c;就像在浩渺的科技海洋中&#xff0c;发现了一座指引方向的灯塔。初次听闻这本书&#xff0c;是在一次技术交流会…

【和春笋一起学C++】文本文件I/O

在windows系统中读取键盘的输入和在屏幕上显示输出统称为&#xff1a;控制台输入/输出。把读取文本文件和把字符输出到文本文件中统称为&#xff1a;文本文件I/O。 目录 1. 输出文本文件 2. 读取文本文件 1. 输出文本文件 把字符输出到文本文件中和输出到控制台很相似&#x…

【C#】WinForm自定义控件及窗体

前言 WinForm&#xff08;Windows Forms&#xff09;是Microsoft.NET框架中的技术&#xff0c;用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…

Live555+Windows+MSys2 编译Androidso库和运行使用

下载 wget http://www.live555.com/liveMedia/public/live555-latest.tar.gz tar -xzvf live555-latest.tar.gz加入版本控制 git init git add . git commit -a -m "first init" git log修改config.android-arm64 cd live vim config.android-arm64 ./genMakefile…