【C语言】深入剖析 C 语言中数组与指针的紧密联系及高效应用


在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: C语言

文章目录

  • 💯前言
  • 💯例一:指针偏移遍历数组
    • 1.1 代码回顾
    • 1.2 代码分析
    • 1.3 理论扩展:数组与指针的关系
    • 1.4 数组与指针的应用场景
  • 💯例二:自定义 `strlen` 函数
    • 2.1 代码回顾
    • 2.2 代码分析
    • 2.3 内存布局与指针差值
    • 2.4 理论扩展:指针差值
    • 2.5 动态字符串处理与指针的优势
  • 💯例三:指针方式遍历数组
    • 3.1 代码回顾
    • 3.2 代码分析
    • 3.3 输出分析
    • 3.4 理论扩展:指针和数组名的用法
  • 💯小结


在这里插入图片描述


💯前言

  • 在 C 语言中,数组和指针之间有着极为紧密的联系数组名可以看作是指向数组首元素的常量指针,因此指针可以被灵活地用于遍历数组访问数组中的任意元素。理解这种紧密联系,对于掌握 C 语言的内存管理编程效率至关重要。
    C
    在这里插入图片描述

💯例一:指针偏移遍历数组

在这里插入图片描述


1.1 代码回顾

在这里插入图片描述
首先,我们来看第一个例子,展示如何通过指针偏移遍历数组并打印每个元素:

int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  // 定义一个含有 10 个整数的数组
int* p = &arr[0];                              // 定义一个指针 p,指向数组的首元素
int i = 0;                                     // 定义一个计数变量 i
int sz = sizeof(arr) / sizeof(arr[0]);         // 计算数组的大小(元素个数)
for (i = 0; i < sz; i++)                       // 遍历数组
{printf("%d ", *(p + i));                   // 通过指针偏移访问数组的每一项并打印
}

1.2 代码分析

在这里插入图片描述

  • 定义数组

    • int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    • 此处定义了一个包含 10 个整数的数组,内容为 110,并将其存储在连续的内存空间中。
  • 定义指针

    • int* p = &arr[0];
    • 定义一个指针 p,并将其初始化为指向数组 arr 的首元素(即 arr[0] 的地址)。实际上,数组名 arr 本身就可以被当作指向数组首地址的指针使用。
  • 计算数组大小

    • int sz = sizeof(arr) / sizeof(arr[0]);
    • 使用 sizeof(arr) 得到整个数组的字节大小,再用 sizeof(arr[0]) 得到单个元素的大小,将两者相除得到数组的元素个数 sz。在此例中,sizeof(arr) 返回整个数组的总字节数(10 个整数的大小),而 sizeof(arr[0]) 返回单个整数的大小,因此 sz 最终为 10
  • 通过指针偏移遍历数组

    • for (i = 0; i < sz; i++)
    • 通过一个 for 循环,变量 i0 遍历到 sz - 1,确保我们访问数组中的每个元素。
    • *(p + i) 使用指针偏移来访问数组中的元素:
      • i = 0 时,*(p + 0) 等于 *p,即 arr[0]
      • i = 1 时,*(p + 1) 等于 arr[1],以此类推。指针偏移是通过增加地址偏移量来实现的,每次增加的大小取决于指针所指类型的大小(对于 int*,每次增加 4 字节,假设 int 为 4 字节)。
  • 打印结果

    • printf("%d ", *(p + i));
    • 每次循环中,指针访问数组中的一个元素并打印出来,最终输出:
    1 2 3 4 5 6 7 8 9 10
    

1.3 理论扩展:数组与指针的关系

在这里插入图片描述
在 C 语言中,数组和指针之间有极为紧密的联系:

  • 数组名作为指针

    • 数组名本身通常可以看作指向数组首元素的常量指针。例如,arr 可以被理解为指向 arr[0] 的指针,其类型为 int*。但需要注意的是,数组名是一个常量指针,不能被修改为指向其他位置。
  • 指针偏移访问

    • 可以通过指针偏移来访问数组中的元素,例如 *(arr + i) 等价于 arr[i]。这种方式的本质是通过指针运算,在原始指针的基础上加上偏移量,以获取目标元素的地址并解引用。
  • 指针与下标的等价性

    • arr[i] 本质上是通过指针偏移实现的,即 *(arr + i)。这种等价性使得开发者可以灵活地选择使用数组下标或者指针运算来访问数组元素。

这种紧密关系使得我们在许多场景下能够通过指针高效地操作数组,提升代码的灵活性和效率。指针的使用使得代码更加贴近底层硬件,能够直接控制内存和数据的存取方式,尤其在需要直接操作内存或者处理动态数据结构时,指针的优势更为明显。


1.4 数组与指针的应用场景

在这里插入图片描述
指针与数组的结合在操作大规模数据、动态分配数组、编写底层驱动程序等场景中有着极为广泛的应用。

例如,在遍历一个大型数组时,使用指针遍历通常比使用数组下标访问更加高效。这是因为指针的移动操作通常只需要一次加法运算,而数组下标访问往往涉及更多的地址计算。此外,指针与函数结合可以实现动态参数的传递,避免数组的大量拷贝。

在系统编程和嵌入式开发中,指针的直接内存访问能力至关重要。指针使程序能够直接控制硬件,精确地管理内存的使用。例如,在实现数据缓冲区(buffer)或访问硬件寄存器时,使用指针能够提供更加高效和灵活的操作方式。


💯例二:自定义 strlen 函数

在这里插入图片描述


2.1 代码回顾

在这里插入图片描述
第二个例子展示了如何实现一个类似于标准库 strlen 的自定义函数,用来计算字符串的长度。

int my_strlen(char* str)
{char* start = str;          // 定义指针 start,指向字符串的起始位置while (*str != '\0')        // 遍历字符串,直到遇到终止符 '\0'str++;                  // 将指针 str 向后移动return str - start;         // 返回 str 和 start 的差值,即字符串的长度
}int main()
{char arr[] = "abcdef";      // 定义一个字符串数组,内容为 "abcdef"int len = my_strlen(arr);   // 调用 my_strlen 函数计算字符串长度printf("%d\n", len);        // 打印字符串长度return 0;
}

2.2 代码分析

在这里插入图片描述

  • 函数定义

    • 函数 my_strlen 用于计算字符串的长度。
    • 输入参数是一个指向字符串的指针 char* str
  • 定义起始指针

    • char* start = str;
    • 定义一个指针 start,将其初始化为指向输入字符串的起始位置。这个指针用于记录字符串的开始位置,以便后续计算长度。
  • 遍历字符串

    • while (*str != '\0')
    • 使用 while 循环遍历字符串,直到遇到终止符 \0\0 作为字符串的结束标志,是 C 语言中表示字符串终止的特殊字符。
    • str++ 将指针向后移动,逐一访问字符串中的每个字符,直到遇到 \0 停止。
  • 计算并返回长度

    • return str - start;
    • 使用指针差值计算字符串的长度。
    • str 最终指向终止符 \0,而 start 指向字符串的起始位置。
    • str - start 表示从起始位置到终止符之间的字符个数,即字符串的长度。这种指针差值计算的方法非常高效,因为它避免了显式的计数变量。

2.3 内存布局与指针差值

在这里插入图片描述
字符串在内存中被存储为一个字符数组,并以特殊的终止符 \0 作为结束标志。下面是字符串 “abcdef” 在内存中的布局:

[a] [b] [c] [d] [e] [f] [\0]

指针 str 从起始位置开始,逐一移动到每个字符,最终停在 \0 的位置。str - start 的值为 6,表示字符串 “abcdef” 的长度。

这种实现方式充分利用了指针运算的优势,通过内存地址的差值直接计算长度,而不需要额外的计数变量,从而使代码更为简洁高效。


2.4 理论扩展:指针差值

在这里插入图片描述

  • 指针运算

    • 指针的减法运算用于计算两个指针之间的距离。对于同一类型的指针,str - start 返回这两个指针之间的元素个数,而不是字节数。在此例中,指针差值代表字符的个数,因此可以返回字符串的长度。
  • 指针的高效性

    • 指针运算是 C 语言中的一大优势,允许程序员高效地操作数组和字符串。通过指针直接进行内存访问,避免了数组下标的使用,减少了计算开销,使得代码更加简洁和高效。
  • 字符数组与终止符

    • 字符串在 C 语言中以 \0 作为终止符,这使得字符串处理变得更为简单和方便。通过检测 \0,我们可以确定字符串的结束位置,而指针可以非常方便地移动到该位置并通过差值计算字符串长度,这是标准库 strlen 的核心思想。

2.5 动态字符串处理与指针的优势

在这里插入图片描述
在处理动态字符串时,指针的灵活性尤为重要。例如,在需要拼接多个字符串或者处理变长字符串的场景中,使用指针可以有效减少不必要的数据拷贝。通过直接操作指针,我们可以遍历、查找或修改字符串中的字符,而不需要为每次操作重新计算数组下标。

此外,指针还可以用于字符串的比较与查找。例如,在实现字符串查找函数 strstr 时,可以通过指针在两个字符串中逐字符比较,直到找到匹配的子字符串。这种直接的内存操作使得代码的性能显著提升,尤其是在需要频繁处理字符串操作的场合。


💯例三:指针方式遍历数组

在这里插入图片描述


3.1 代码回顾

在这里插入图片描述
第三个例子展示了通过指针遍历数组的另一种方式。

int main()
{int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  // 定义一个含有 10 个整数的数组int sz = sizeof(arr) / sizeof(arr[0]);       // 计算数组的大小(元素个数)int* p = arr;                                // 定义指针 p,指向数组首元素(等价于 &arr[0])while (p < arr + sz)                         // 遍历指针,从起始位置到结束位置{printf("%d ", *p);                       // 打印当前指针所指向的值p++;                                     // 指针后移,指向下一个元素}return 0;
}

3.2 代码分析

在这里插入图片描述

  • 定义数组与指针

    • 定义一个整数数组 arr 和一个指向数组的指针 p
    • int* p = arr; 将指针 p 指向数组的第一个元素。数组名 arr 在表达式中作为指针使用,指向数组的起始地址。
  • 计算数组大小

    • 使用 sizeof(arr) 得到整个数组的字节大小,sizeof(arr[0]) 得到单个元素的大小,两者相除得到数组的元素个数 sz
  • 通过指针遍历数组

    • while (p < arr + sz) 表示指针 p 不能超过数组的结尾位置。arr + sz 表示数组最后一个元素的下一个位置,这个位置不再是数组的一部分。
    • 每次循环打印当前指针 p 所指向的值,并使指针 p 后移,直到指针超出数组范围。

3.3 输出分析

在这里插入图片描述
该代码片段会打印数组中的所有元素,输出结果为:

1 2 3 4 5 6 7 8 9 10

3.4 理论扩展:指针和数组名的用法

在这里插入图片描述

  • 数组名的特性

    • 在 C 语言中,数组名可以看作是常量指针,指向数组的第一个元素。因此,可以将数组名赋值给一个指针,然后通过指针来操作数组。但是需要注意的是,数组名本身是一个不可修改的地址,而指针变量可以修改。
  • 指针遍历的优势

    • 在循环条件 p < arr + sz 中,arr + sz 表示数组的最后一个元素的下一个位置。通过这种方式,我们可以很方便地使用指针来遍历数组,而不用担心数组下标越界的问题。
  • 代码效率的提升

    • 通过指针遍历数组,可以避免使用数组下标,这样的代码通常更为高效,尤其是在涉及大量数据的场景中。指针的移动操作本质上是对内存地址的加减法操作,这种直接的内存访问方式比通过数组下标访问更加高效。
  • 指针与循环的结合

    • 使用指针与循环结合遍历数组,是 C 语言中的常见模式。这种方式使得代码更加简洁,同时也更接近硬件的操作,使得程序运行更快。尤其是在编写底层代码或需要进行内存优化的程序时,指针的灵活性和高效性是非常重要的。
  • 指针与动态数组

    • 在某些情况下,我们需要动态分配数组的大小,无法在编译时确定。这时候,指针的作用尤为明显。通过动态内存分配函数(如 malloc),我们可以创建任意大小的数组,并通过指针进行操作。这种方式在需要处理可变大小的数据时非常有用,例如,在数据采集或缓存系统中。
  • 指针与递归的结合

    • 指针还可以与递归函数结合,用于实现复杂的数据结构操作,如链表、树等。在递归过程中,指针的传递使得函数可以直接操作数据结构中的节点,而不需要创建额外的拷贝,从而提高了程序的效率。

💯小结

  • 在这里插入图片描述本文详细探讨了 C 语言中数组与指针的紧密关系,通过多个实例展示了 指针偏移访问数组自定义字符串函数指针遍历数组 等常见场景的实现方式及其内在原理。指针作为 C 语言的核心特性,不仅使代码更加 灵活高效,还在 内存管理动态数据结构底层硬件控制 等方面展现了其独特的优势。通过深入理解 数组与指针的关系,程序员可以更高效地编写代码,提升程序的 性能可扩展性

在这里插入图片描述


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

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

相关文章

基于SpringBoot的数据结构系统设计与实现(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

算法训练-双指针

双指针 leetcode392. 判断子序列 法一&#xff1a;动态规划 法二&#xff1a;双指针 leetcode876. 链表的中间结点 法一&#xff1a;链表数组 法二&#xff1a;快慢指针 leetcode160. 相交链表 法一&#xff1a;双指针 leetcode167. 两数之和 II - 输入有序数组 法一&…

零基础学指针(上)

系列文章目录 &#x1f388; &#x1f388; 我的CSDN主页:OTWOL的主页&#xff0c;欢迎&#xff01;&#xff01;&#xff01;&#x1f44b;&#x1f3fc;&#x1f44b;&#x1f3fc; &#x1f389;&#x1f389;我的C语言初阶合集&#xff1a;C语言初阶合集&#xff0c;希望能…

shell编程之sed

sed 是一种流编辑器&#xff0c;它是文本处理中非常有用的工具&#xff0c;能够完美的配合正则表达式使用&#xff0c;处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为模式空间&#xff0c;接着用sed 命令处理缓冲区中的内容&#xff0c;处理完成 后&…

一文学习开源框架OkHttp

OkHttp 是一个开源项目。它由 Square 开发并维护&#xff0c;是一个现代化、功能强大的网络请求库&#xff0c;主要用于与 RESTful API 交互或执行网络通信操作。它是 Android 和 Java 开发中非常流行的 HTTP 客户端&#xff0c;具有高效、可靠、可扩展的特点。 核心特点 高效…

多目标优化算法:多目标极光优化算法(MOPLO)求解ZDT1、ZDT2、ZDT3、ZDT4、ZDT6,提供完整MATLAB代码

一、极光优化算法 极光优化算法&#xff08;Polar Lights Optimization, PLO&#xff09;是2024年提出的一种新型的元启发式优化算法&#xff0c;它从极光这一自然现象中汲取灵感。极光是由太阳风中的带电粒子在地球磁场的作用下&#xff0c;与地球大气层中的气体分子碰撞而产…

【贪心算法第二弹——2208.将数组和减半的最小操作数】

1.题目解析 题目来源 2208.将数组和减半的最小操作数——力扣 测试用例 2.算法原理(贪心策略) 3.实战代码 class Solution { public:int halveArray(vector<int>& nums) {priority_queue<double> hash;double sum 0.0;for(auto e : nums){hash.push(e);sum …

2024最新python使用yt-dlp

2024最新python使用yt-dlp下载YT视频 1.获取yt的cookie1&#xff09;google浏览器下载Get cookies.txt LOCALLY插件2&#xff09;导出cookie 2.yt-dlp下载[yt-dlp的GitHub地址](https://github.com/yt-dlp/yt-dlp?tabreadme-ov-file)1&#xff09;使用Pycharm(2024.3)进行代码…

深入理解下oracle 11g block组成

深层次说&#xff0c;oracle数据库的最少组成单位应该是块&#xff0c;一般默认情况下&#xff0c;oracle数据库的块大小是8kb&#xff0c;其中存储着我们平常所需的数据。我们在使用过程中&#xff0c;难免会疑问道&#xff1a;“oracle数据块中到底是怎样组成的&#xff0c;平…

2024年12月Gesp七级备考知识点拾遗第一期(图的定义及遍历)

目录 总序言 知识点拾遗​编辑 度数 环 二叉树 图的遍历 深度优先 广度优先 连通与强连通 有什么不同 构成分别至少需要几条边&#xff08;易错题&#xff09;&#xff1f; 无向连通图 有向强连通图 完全图 什么是完全图 无向完全图最少边数 有向完全图最少边…

家庭智慧工程师:如何通过科技提升家居生活质量

在今天的数字化时代&#xff0c;家居生活已经不再只是简单的“住”的地方。随着物联网&#xff08;IoT&#xff09;、人工智能&#xff08;AI&#xff09;以及自动化技术的快速发展&#xff0c;越来越多的家庭开始拥抱智慧家居技术&#xff0c;将他们的家变得更加智能化、便捷和…

图像处理实验报告

实验一 图像处理的MATLAB基础 实验目的&#xff1a;熟悉数字图象处理的基本软件工具和操作 实验内容&#xff1a;Matlab应用复习&#xff0c;矩阵产生、操作&#xff1b;矩阵运算以及字符运算。 1.利用增量产生向量[0,2,4,6,8,10]。 2.利用magic(n)函数产生7维魔鬼矩阵A&am…

SpringBoot+SpringCloud面试题整理附答案

什么是SpringBoot&#xff1f; 1、用来简化spring初始搭建和开发过程使用特定的方式进行配置(properties或者yml文件) 2、创建独立的spring引用程序main方法运行 3、嵌入Tomcat无需部署war包&#xff0c;直接打成jar包nohup java -jar – & 启动就好 4、简化了maven的配置 …

Linux之管道,system V的共享内存,消息队列和信号量

Linux之管道&#xff0c;systemV共享内存和信号量 一.进程间通信1.1进程间通信的目的1.2进程间通信的方式 二.管道2.1管道的概念2.2匿名管道2.3命名管道 三.system V3.1共享内存3.2消息队列3.3信号量 一.进程间通信 在我们之前有关Linux指令的学习时我们使用过“|”这个命令&a…

Figma入门-基本操作制作登录页

Figma入门-基本操作制作登录页 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&am…

Django实现智能问答助手-数据库方式读取问题和答案

扩展 增加问答数据库&#xff0c;通过 Django Admin 添加问题和答案。实现更复杂的问答逻辑&#xff0c;比如使用自然语言处理&#xff08;NLP&#xff09;库。使用前端框架&#xff08;如 Bootstrap&#xff09;增强用户界面 1.注册模型到 Django Admin&#xff08;admin.py…

SQL注入--文件读写注入--理论

什么是文件读写注入&#xff1f; MySQL中有 读取文件的函数&#xff1a;load_file() 写入文件的函数&#xff1a;Into outfile&#xff08;能写入多行&#xff0c;按格式输出&#xff09;和 into dumpfile&#xff08;只能写入一行且没有输出格式&#xff09; 利用这些函数在S…

《最小生成树算法详解:Kruskal的优雅实现》

前置知识和本篇介绍 前置知识&#xff1a; 数据结构-优先级队列&#xff0c; 数据结构-并查集。 Kruskal算法不需要建图&#xff0c; 因此不会建图的模板也没事。 本篇介绍一最小生成树的概念和Kruskal算法。 有关prim算法&#xff08;另一种最小生成树的算法&#xff09;&am…

云计算-华为HCIA-学习笔记

笔者今年7月底考取了华为云计算方向的HCIE认证&#xff0c;回顾从IA到IE的学习和项目实战&#xff0c;想整合和分享自己的学习历程&#xff0c;欢迎志同道合的朋友们一起讨论&#xff01; 第二章&#xff1a;服务器基础 服务器是什么&#xff1f; 服务器本质上就是个性能超强的…

uniapp接入高德地图

下面代码兼容安卓APP和H5 高德地图官网&#xff1a;我的应用 | 高德控制台 &#xff0c;绑定服务选择《Web端(JS API)》 /utils/map.js 需要设置你自己的key和安全密钥 export function myAMap() {return new Promise(function(resolve, reject) {if (typeof window.onLoadM…