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

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

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


在这里插入图片描述



http://www.kler.cn/a/412013.html

相关文章:

  • java写一个石头剪刀布小游戏
  • ASP网络安全讲述
  • vscode下面python调试报错ImportError: cannot import name ‘Literal‘ from ‘typing‘
  • 学习笔记032——Spring学习笔记
  • 网络安全风险评估
  • 【Python】九大经典排序算法:从入门到精通的详解(冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序、计数排序、基数排序、桶排序)
  • C/C++绘制爱心
  • 【论文阅读】WGSR
  • 紫光档案管理系统 mergeFile SQL注入漏洞复现
  • MySQL闪回恢复:轻松应对数据误删,数据安全有保障
  • 16:00面试,16:08就出来了,问的问题有点变态。。。
  • 实时数据开发 | 一文理解Flink窗口机制
  • 算法学习笔记(十):位运算、数论等
  • Java多态的优势和弊端
  • 入门岛-L0G1000
  • 【H2O2|全栈】JS进阶知识(十)ES6(6)
  • golang版本管理工具:scoop使用
  • Docker 技术:构建高效容器化环境的关键
  • QML TableView 实例演示 + 可能遇到的一些问题(Qt_6_5_3)
  • 裸金属 GPU 上线!DigitalOcean 提供单用户独享H100资源
  • 蓝桥杯不知道叫什么题目
  • 微信小程序加载商品首页数据时,页码没有更新,老是page=1。
  • LAMP环境的部署
  • 【大数据学习 | Spark-Core】RDD的缓存(cache and checkpoint)
  • 网络安全防范课后参考答案
  • vue3 发送 axios 请求时没有接受到响应数据