【C语言】指针与数组的例题详解:深入分析与高级用法


在这里插入图片描述

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

文章目录

  • 💯前言
  • 💯题目一
    • 详细分析与解答
      • 代码逐步解析
  • 💯进一步优化和拓展
    • 1. 指针与数组的关系
    • 2. 指针运算的注意事项
    • 3. 常见的错误和陷阱
    • 4. 拓展:指针操作的应用场景
    • 5. 指针与内存地址的深入理解
  • 💯题目二
    • 数组传递给函数时的特性
    • 数组退化为指针
    • 指针遍历字符串
    • 输出结果
    • 数组与指针的区别
    • 指针与函数参数的灵活性
  • 💯小结


在这里插入图片描述


💯前言

  • C语言作为一门经典的编程语言,以其高效灵活接近底层硬件的特点,广泛应用于嵌入式开发操作系统内核以及高性能计算等领域。然而,C语言的灵活性也带来了学习过程中的诸多挑战,特别是在指针与数组内存管理以及字符串处理等核心概念的掌握上。
    本文通过对经典题目的逐步解析,深入探讨 C语言关键特性,并结合代码实例详细讲解其底层原理实际应用。希望通过这样的方式,帮助读者更全面地理解 C语言的强大之处,从而在编程中更加得心应手
    C语言
    在这里插入图片描述

💯题目一

在这里插入图片描述
题目描述:

如下程序段的输出结果为:

unsigned long pulArray[] = {6, 7, 8, 9, 10};
unsigned long *pulPtr;
pulPtr = pulArray;
*(pulPtr + 3) += 3;
printf("%d, %d\n", *pulPtr, *(pulPtr + 3));

详细分析与解答

在这里插入图片描述
这道题考察的是指针和数组的操作,特别是指针的算术运算以及对数组元素的修改。接下来,我们逐步分析程序的执行过程,以得出正确的答案。


代码逐步解析

在这里插入图片描述
首先,我们逐行解释代码,并深入理解每一步的执行过程。

unsigned long pulArray[] = {6, 7, 8, 9, 10};

这一行定义了一个 unsigned long 类型的数组 pulArray,它包含 5 个元素,分别是 {6, 7, 8, 9, 10}。数组在内存中会被分配一块连续的存储空间,每个元素占据一个存储单元,内存地址按元素依次递增。

unsigned long *pulPtr;

这里定义了一个 unsigned long 类型的指针 pulPtr,它将用于指向数组中的元素。

pulPtr = pulArray;

这行代码将 pulPtr 指向数组 pulArray 的首地址。也就是说,pulPtr 现在指向 pulArray[0],即值为 6 的位置。

*(pulPtr + 3) += 3;

这一行代码对数组中的一个元素进行了修改。首先,我们来看 pulPtr + 3

  • pulPtr 是指向数组第一个元素的指针。
  • pulPtr + 3 表示将指针偏移到数组中第 4 个元素的地址(注意数组的索引是从 0 开始的)。

通过 *(pulPtr + 3) 取出该地址所指向的值,也就是数组的第 4 个元素 9。然后 += 3 操作将该值增加 3,变为 12。因此,数组 pulArray 现在的值变成了 {6, 7, 8, 12, 10}

printf("%d, %d\n", *pulPtr, *(pulPtr + 3));
  • *pulPtr 取出指针 pulPtr 所指向的值,即数组的第一个元素 6
  • *(pulPtr + 3) 取出指向第 4 个元素的值,现在这个值已经被修改为 12

因此,printf 最终输出的结果是:

6, 12

💯进一步优化和拓展

在这里插入图片描述
为了更好地理解这个题目,我们来探讨一些与此相关的重要概念,包括指针运算、数组与指针的关系,以及 C 语言中的一些常见错误和陷阱。


1. 指针与数组的关系

在这里插入图片描述
在 C 语言中,数组名在大多数情况下会退化为指向其首元素的指针。这意味着 pulArray 可以被看作是指向 pulArray[0] 的地址。因此,以下代码:

pulPtr = pulArray;

可以理解为让指针 pulPtr 指向数组的起始地址。此后,通过指针的偏移量我们可以访问数组中的其他元素,例如:

  • pulPtr + 1 指向 pulArray[1]
  • pulPtr + 3 指向 pulArray[3],即值为 9 的位置

2. 指针运算的注意事项

指针的运算是基于指针所指向的数据类型大小的。例如,对于一个 unsigned long 类型的指针,每次加 1 实际上会增加 sizeof(unsigned long) 个字节,而不是简单地增加 1 个字节。这种特性在访问数组元素时尤为重要,因为它能保证指针准确地指向下一个元素,而不是指向任意的中间位置。

在这里插入图片描述


3. 常见的错误和陷阱

  • 数组越界访问:在使用指针操作数组时,如果偏移量超出了数组的边界,就会发生越界访问,导致未定义行为。这种错误在 C 语言中非常常见且很难调试。
  • 未初始化指针:如果指针没有正确初始化(例如没有指向有效的内存地址),对其解引用会导致严重的错误,例如段错误(Segmentation Fault)。在这道题中,指针 pulPtr 在赋值之前指向了有效的数组,因此没有这个问题。

在这里插入图片描述


4. 拓展:指针操作的应用场景

指针的运算和数组的结合在许多场景中都非常有用,例如:

  • 遍历数组:通过指针偏移量来遍历数组中的元素,通常比使用数组索引更加高效,尤其是在嵌入式系统中,由于资源有限,高效地访问数据显得尤为重要。
  • 动态内存管理:在堆上分配内存时(例如使用 malloc),返回的是指向已分配内存的指针,之后对这块内存的操作大多需要通过指针进行。
  • 字符处理与字符串操作:指针在字符串处理方面非常高效,通过指针可以直接访问字符数组中的每一个字符,进行遍历、修改等操作。
    在这里插入图片描述

5. 指针与内存地址的深入理解

指针不仅仅是一个变量,它实际上是一个存储内存地址的变量。理解指针与内存地址之间的关系,对于掌握指针的高级用法至关重要。例如,指针的偏移量取决于它所指向的数据类型大小,这使得 C 语言中的指针非常灵活且强大。在涉及复杂数据结构(如链表、树结构)时,指针的使用必不可少,可以有效地管理和操纵动态数据。
在这里插入图片描述


💯题目二

在另一段代码中,定义了一个自定义的字符串长度函数 my_strlen,其目的是计算一个字符串的长度。

代码如下:

int my_strlen(char* arr) {
    int count = 0;
    while (*arr != '\0') {
        count++;
        arr++;
    }
    return count;
}

int main3() {
    char arr[] = "今天是2024年11月26日";
    printf("%d", my_strlen(arr));
    return 0;
}

在这里插入图片描述


数组传递给函数时的特性

main3() 函数中,定义了一个字符数组 arr,并将其传递给了 my_strlen 函数。这个过程中,我们并不需要显式定义一个指针,原因在于 C 语言中的数组退化特性。
在这里插入图片描述


数组退化为指针

  • 在调用 my_strlen(arr) 时,数组名 arr 会自动退化为一个指向其首元素的指针。也就是说,my_strlen 实际上接收的是 arr 的首地址(一个 char* 类型的指针),这使得 my_strlen 可以遍历整个字符串直到遇到字符串的终止符 \0
  • 这种退化机制在 C 语言中被广泛使用,使得函数可以方便地操作数组数据,而不需要将整个数组拷贝传递,这样也提高了效率。
  • 此外,数组退化为指针的特性在很多场景下可以提高代码的通用性和复用性,因为指针可以指向不同的数组,从而实现对不同数据的操作。
    在这里插入图片描述

指针遍历字符串

my_strlen 函数中,使用指针 arr 来遍历整个字符串:

while (*arr != '\0') {
    count++;
    arr++;
}

这里,*arr 取出当前字符的值,并判断是否为终止符 \0。如果不是,就将 arr 向后移动一个字符位置,并增加计数器 count。这种方式让函数能够有效地计算出字符串的长度,直到遇到字符串的结束符。
在这里插入图片描述


输出结果

在这里插入图片描述

main3() 中,字符串为 "今天是2024年11月26日"my_strlen(arr) 返回 20,即字符串的长度。
在这里插入图片描述


数组与指针的区别

  • 数组是连续内存块arr 是一块连续的内存空间,包含所有字符。
  • 指针是地址变量:在传递给函数时,arr 自动退化为指针,这个指针指向数组的首元素。通过指针操作,可以灵活地遍历或操作数组中的每一个元素。
  • 指针的灵活性:指针可以指向任意内存地址(只要是有效的内存),这使得它在处理动态数据结构(如链表、树等)时特别有用。相比之下,数组的内存是固定的,数组名只能指向这一块特定的内存。
    在这里插入图片描述

指针与函数参数的灵活性

在许多情况下,使用指针来传递参数,可以更高效地操作数据。特别是在处理大型数据结构时,直接传递数组的首地址比拷贝整个数组更节省内存和时间。此外,通过指针,我们可以修改数组中的内容,而不仅仅是读取它们的值,这也是指针传递的一个重要优势。
在这里插入图片描述


💯小结

  • 在这里插入图片描述
    在第一段代码中,定义指针 pulPtr 让我们可以通过指针偏移来访问和修改数组中的元素。这种操作在 C 语言中非常灵活和常用

  • 在第二段代码中,数组 arr 被传递给 my_strlen 函数,数组名自动退化为指针,从而允许函数通过指针遍历整个字符串。这种机制使得字符串处理非常高效

  • 指针与数组C 语言中是非常重要且强大的工具,能够有效地进行内存操作数据处理。在使用时需要小心指针的边界和初始化问题,以避免常见的错误

  • 指针的灵活性使得它在许多场景下成为不可替代的工具,尤其是在涉及复杂数据结构动态内存管理时。


在这里插入图片描述


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java】ArrayList与LinkedList详解!!!

目录 一🌞、List 1🍅.什么是List? 2🍅.List中的常用方法 二🌞、ArrayList 1🍍.什么是ArrayList? 2🍍.ArrayList的实例化 3🍍.ArrayList的使用 4🍍.ArrayList的遍…

【SQL Server】华中农业大学空间数据库实验报告 实验六 视图

1.实验目的 通过课堂理论学习与实验课的实际操作,充分理解视图的相关概念,作用,以及特点,视图中定义的是对一个或多个基本表的查询语句,其本身并不保存数据,所有的数据都存储在数据库的表中,因…

javaweb-day01-html和css初识

html:超文本标记语言 CSS:层叠样式表 1.html实现新浪新闻页面 1.1 标题排版 效果图: 1.2 标题颜色样式 1.3 标签内颜色样式 1.4设置超链接 1.5 正文排版 1.6 页面布局–盒子 (1)盒子模型 (2)页面布局…

【Android】webview常用方法和使用

文章目录 前言一、常见用法二、基础属性webView的常用方法WebViewClient的常用方法WebChromeClient的常用方法WebSettings的相关方法 三、加载流程和事件回调四、webview和JS之间的互相调用总结 五、参考链接 前言 最近项目又用到了webview,在回顾复习一次webview相…

【微服务架构】Kubernetes与Docker在微服务架构中的最佳实践(详尽教程)

文章目录 什么是微服务架构Docker在微服务中的应用Docker基础Docker的核心组件 Docker在微服务中的优势 Kubernetes在微服务中的应用Kubernetes基础Kubernetes的核心组件 Kubernetes在微服务中的优势 Kubernetes与Docker的集成最佳实践容器化微服务服务发现与负载均衡自动化部署…

深入了解JDK动态代理

什么是JDK动态代理 (有动态代理,就有静态代理,参见:多线程03--静态代理模式_runnable接口静态代理模式-CSDN博客) JDK动态代理是Java提供的一种动态生成代理对象的机制,允许在运行时创建一个实现了指定接口…

C#基础56-60

56.字符数组x中存有任意一串字符;串中的所有小写字母改写成大写字母,如果是大写字母改为小写字母,其他字符不变。最后把已处理的字符串仍重新存入字符数组x中,最后调用函数把结果输出到控制台中。 57.求出100以上1000以内所有个位…

华为IPD流程管理体系L1至L5最佳实践-解读

该文档主要介绍了华为IPD流程管理体系,包括流程体系架构、流程框架实施方法、各业务流程框架示例以及相关案例等内容,旨在帮助企业建立高效、规范的流程管理体系,实现业务的持续优化和发展。具体内容如下: 1. 华为流程体系概述 -…

Edge浏览器保留数据,无损降级退回老版本+禁止更新教程(适用于Chrome)

3 个月前阿虚就已经写文章告警过大家,Chromium 内核的浏览器将在 127 以上版本开始限制仍在使用 Manifest V2 规范的扩展:https://mp.weixin.qq.com/s/v1gINxg5vMh86kdOOmqc6A 像是 IDM、油猴脚本管理器、uBblock 等扩展都会受到影响,后续将无…

DevOps引领数字化转型新趋势

DevOps帮助数字化转型 在数字化转型的大潮中,DevOps作为一种文化、运动和实践,已经成为推动企业快速适应市场变化、提高竞争力的关键因素。DevOps的核心在于打破开发(Development)和运维(Operations)之间的…

ctfshow

1,web21 Basic认证采用Base64加密方式,Base64解码字符串发现是 用户名:密码 的格式进行Base64编码。 密码shark63 2,web22 用 子域名扫描器 扫出flag.ctf.show拿到flag,但这个域名已经没了所以就直接交的官方提供的flag。 3,web23 这段PHP代码是一个简单…

从 0 到 1 掌握部署第一个 Web 应用到 Kubernetes 中

文章目录 前言构建一个 hello world web 应用项目结构项目核心文件启动项目 检查项目是否构建成功 容器化我们的应用编写 Dockerfile构建 docker 镜像推送 docker 镜像仓库 使用 labs.play-with-k8s.com 构建 Kubernetes 集群并部署应用构建 Kubernetes 集群环境编写部署文件 总…

数据结构与算法——1120——时间空间效率问题求边界值

目录 1、效率问题 1、时间复杂度 1、O(1) 2、O(n) 3、O(n) 或O(n*log2n)——n倍的log以2为底n的对数 例题 4、O(n) 2、空间复杂度 3、数组和链表 2、面试题之求边界值 题目 解答 (1)-i (2)~i (3&#x…

爬虫与反爬-Ja3指纹风控(Just a moment...)处理方案及参数说明

概述:本文将针对 Ja3 指纹检测风控进行处理,举例了一个案例并使用两种不同的破解方案进行突破,同时深入了解指纹间不同字符所代表的含义 指纹检测背景: 1、每一个设备、软件都有独属于自己的设备信息、版本号、加密算法、椭圆算…

【JUC-JMM】Java Memory Model Java内存模型

Java内存模型--JMM 一、JMM是什么?二、Happens-Before原则三、JMM有什么用? 一、JMM是什么? JMM,全拼Java Memory Model,翻译过来就是Java内存模型。 那么,我们不禁思索,Java内存模型有什么用&…

SpringAI:Java 开发的智能新利器

一、SpringAI 简介 随着人工智能技术的飞速发展,越来越多的开发者开始探索如何将 AI 能力集成到现有的应用中来提升产品的智能化水平。Spring AI 正是为 Java 开发者提供的一款强大的 AI 框架,使得这一集成过程变得前所未有的简单和高效。 本文将深入探…

在Excel中处理不规范的日期格式数据并判断格式是否正确

有一个Excel表,录入的日期格式很混乱,有些看着差不多,但实际多一个空格少一个字符很难发现,希望的理想格式是 1980-01-01,10位,即:“YYYY-mm-dd”,实际上数据表中这样的格式都有 19…

【我在CSDN成长】我的五周年创作纪念日

感叹 五年的时光匆匆而过, 像一阵风,拂过岁月的湖面, 泛起层层涟漪,又悄然离去。 曾经的欢笑与泪水, 那些奋斗的日夜, 如同电影般在脑海中放映, 却已成为遥远的回忆。 五年,说长不长…

Jmeter中的定时器

4)定时器 1--固定定时器 功能特点 固定延迟:在每个请求之间添加固定的延迟时间。精确控制:可以精确控制请求的发送频率。简单易用:配置简单,易于理解和使用。 配置步骤 添加固定定时器 右键点击需要添加定时器的请求…

BERT 详解

BERT简介 BERT(Bidirectional Encoder Representations from Transformers)是由 Google 在 2018 年提出的一种预训练语言模型。BERT 在自然语言处理(NLP)领域取得了重大突破,因为它能够有效地捕捉文本的上下文信息&am…