C语言入门系列:探秘二级指针与多级指针的奇妙世界

2024-06-24 12:20

本文主要是介绍C语言入门系列:探秘二级指针与多级指针的奇妙世界,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一,指针的回忆杀
    • 1,指针的概念
    • 2,指针的声明和赋值
    • 3,指针的使用
      • 3.1 直接给指针变量赋值
      • 3.2 通过*运算符读写指针指向的内存
        • 3.2.1 读
        • 3.2.2 写
  • 二,二级指针详解
    • 1,定义
    • 2,示例说明
    • 3,二级指针与一级指针、普通变量的关系
      • 3.1,与一级指针的关系
      • 3.2,与普通变量的关系,
      • 示例说明
    • 4,二级指针的常见用途
    • 5,二级指针扩展到多级指针
  • 小结

C语言的学习之旅中,二级指针乃至多级指针往往是初学者感到“恶心”却又不得不面对的重要概念。

不过,一旦掌握了它们的精髓,你会发现它们其实并不那么可怕,反而在某些高级应用中显得尤为强大和灵活。

一,指针的回忆杀

1,指针的概念

指针就是内存地址,内存地址指向内存中的一个单元,这个单元就像一个个小房间,里面住着某种数据,可能是整数,可能是浮点数,可能是字符,也可能是另一个指针。

为了方便理解和记忆,可以把指针当作酒店房间号。

指针作为一种特殊的数据,也需要存储在内存中。

2,指针的声明和赋值

C语言通过指针变量为指针开辟一块内存。

int num = 10;
int* p;
p = #

如上代码,定义了指针变量p,声明变量时如果带有*号,说明这个变量是指针变量。

这个*号的作用是告诉编译器,这个指针变量p要存储的是变量num的值的内存地址,这个内存地址从形式上看也没什么特别,就是一个整数。

上面的代码中p = #,通过地址运算符&取出变量num的地址,赋给指针变量p

在这里插入图片描述

3,指针的使用

3.1 直接给指针变量赋值

上面的代码中p = # 所示,可以和普通变量一样,直接给指针变量赋值,只是这个值代表的是内存地址而已。

3.2 通过*运算符读写指针指向的内存

3.2.1 读

我们还可以通过* 运算符操作指针指向的内存中的值。

int num = 10;
int* p;
p = #int num2 = *p;

如上代码最后一行int num2 = *p;,运算符*的作用是告诉编译器,找到指针p指向的地址,从中读取数据。

3.2.2 写

当然,可以修改指针指向的内存中的数据。

int num = 10;
int* p;
p = #*p = 100;

上述代码*p = 100;,将指针p指向的内存中数据的值修改为100。注意,此时num的值也变为100了,原因是修改的就是num的内存中的值。

二,二级指针详解

1,定义

二级指针,顾名思义,是指向指针的指针,即一个变量,其类型是指向指针的地址。在C语言中,声明一个二级指针的基本形式如下:

数据类型** pointerName;

这里,**pointerName表示pointerName是一个指向指针的指针,它存储的是一个指针变量的地址。

int num = 10;
int* p;
p = #int** pp = &p;

如上,二级指针变量pp指向的是指针变量p的地址。
在这里插入图片描述

如上图所示,发现二级指针需要跳2级才能获取普通变量内存中的值,这也是二级指针名称的来源。

2,示例说明

以一个整型变量为例:

int a = 12;
int *p = &a;
int **pp = &p;

根据上述定义,我们可以得出以下计算结果:

p == &a
*p == 12
pp == &p
*pp == p
**pp == a

**pp就是获取a变量的值,因为pp指向的是p的地址,而p指向的又是a的地址,所以 **pp 就相当于a

3,二级指针与一级指针、普通变量的关系

3.1,与一级指针的关系

一级指针存储的是普通变量的地址,而二级指针存储的是一级指针的地址。
通过二级指针,我们可以间接访问到一级指针所指向的数据,实现更深层次的间接访问。

3.2,与普通变量的关系,

普通变量是数据存储的最直接形式,通过一级指针,我们增加了一层间接访问;二级指针则在此基础上再增加一层间接性,使得对普通变量的操作更为灵活。

示例说明

#include <stdio.h>
#include <stdlib.h>int main() {int value = 10;int *p = &value; // 一级指针,指向valueint **pp = &p;   // 二级指针,指向一级指针pprintf("Value: %d\n", value);      // 直接访问printf("Value via *p: %d\n", *p);  // 通过一级指针访问printf("Value via **pp: %d\n", **pp); // 通过二级指针访问return 0;
}

此例中,value是一个普通整型变量,p是一级指针指向value,而pp作为二级指针,则指向p。通过**pp,我们依然能最终访问到value的值,展示了指针间接访问的层级关系。

4,二级指针的常见用途

  • 动态内存管理:在动态分配数组时,可以使用二级指针来存储数组的首地址,便于管理和释放内存。
  • 函数参数传递:当希望函数能够修改外部指针变量(而非指针所指向的内容)时,需使用二级指针作为函数参数。
  • 指向指针的指针运算:在复杂数据结构操作中,二级指针提供了对指针进行指针运算的能力,如链表的节点交换、树结构的遍历等。

5,二级指针扩展到多级指针

多级指针的概念基于二级指针进一步扩展,即指针的指针的指针……依此类推。

一句话,多级指针存储的是上一级指针的地址。

虽然在日常编程中较为罕见,但在特定场景下,如复杂的内存管理、高度抽象的数据结构设计或底层系统编程中,多级指针可以提供极大的灵活性。

示例:三级指针

#include <stdio.h>void printViaTriplePointer(int ***triplePtr) {printf("Value via ***triplePtr: %d\n", ***triplePtr);
}int main() {int value = 7;int *p = &value;int **pp = &p;int ***triplePtr = &pp;printViaTriplePointer(triplePtr);return 0;
}

这个例子中,triplePtr是一个三级指针,通过它我们依然能够最终访问到最初定义的value。虽然看起来繁琐,但在特定逻辑或系统级操作中,这样的间接访问机制可能非常有用。

小结

总之,二级指针乃至多级指针虽初看复杂,实则是C语言中处理复杂数据结构和内存管理的强大工具。通过实践和理解,你将逐步揭开它们的神秘面纱,发现其背后的逻辑之美。

这篇关于C语言入门系列:探秘二级指针与多级指针的奇妙世界的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1090131

相关文章

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

java poi实现Excel多级表头导出方式(多级表头,复杂表头)

《javapoi实现Excel多级表头导出方式(多级表头,复杂表头)》文章介绍了使用javapoi库实现Excel多级表头导出的方法,通过主代码、合并单元格、设置表头单元格宽度、填充数据、web下载... 目录Java poi实现Excel多级表头导出(多级表头,复杂表头)上代码1.主代码2.合并单元格3.

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

揭秘世界上那些同时横跨两大洲的国家

我们在《世界人口过亿的一级行政区分布》盘点全球是那些人口过亿的一级行政区。 现在我们介绍五个横跨两州的国家,并整理七大洲和这些国家的KML矢量数据分析分享给大家,如果你需要这些数据,请在文末查看领取方式。 世界上横跨两大洲的国家 地球被分为七个大洲分别是亚洲、欧洲、北美洲、南美洲、非洲、大洋洲和南极洲。 七大洲示意图 其中,南极洲是无人居住的大陆,而其他六个大洲则孕育了众多国家和