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

C语言:指针的进阶讲解

目录

1. 二级指针

1.1 二级指针是什么?

1.2 二级指针的作用

2. 一维数组和二维数组的本质

3. 指针数组

4. 数组指针

5. 函数指针

6. typedef的使用

7. 函数指针数组

7.1 转移表


1. 二级指针

如果了解了一级指针,那二级指针也是可以很好的理解的

1.1 二级指针是什么?

二级指针跟一级指针一样,也是接收地址,但是它存的地址是一级指针的地址

int a = 10;
int* p = &a; //存放a的地址
int** pp = &p;//存放p的地址

 它们的关系就类似这样:

 一级指针能解引用获取到a的值,二级指针也能通过解引用获取a的值,区别就是次数不同而已

int main()
{int a = 10;int* p = &a; int** pp = &p;printf("*p = %d, **pp = %d", *p, **pp);return 0;
}

我们可以理解二级指针用一次 * 就降一级

所以需要两个 * 才能获取到a,第一次的*是得到p

1.2 二级指针的作用

一级指针的作用是可以在函数内部实现两个数的交换

如果只是简单的传参是无法实现两个变量的交换的

#include <stdio.h>void Swap(int a, int b)
{int tmp = a;a = b;b = tmp;
}int main()
{int a = 10;int b = 8;Swap(a, b);printf("a = %d, b = %d\n", a, b);return 0;
}
输出:a = 10, b = 8

 指针就可以实现

#include <stdio.h>void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}int main()
{int a = 10;int b = 8;Swap(&a, &b);printf("a = %d, b = %d\n", a, b);return 0;
}
输出:a = 8, b = 10

一级指针可以实现的东西二级指针当然也能实现了 

#include <stdio.h>void Swap(int** a, int** b)
{int tmp = **a;**a = **b;**b = tmp;
}int main()
{int a = 10;int b = 8;int* pa = &a;int* pb = &b;Swap(&pa, &pb);printf("a = %d, b = %d\n", a, b);return 0;
}

但这样做明显有点小题大做了

前面说了它们是一个分级的关系,那么一级指针能对初始变量做的,二级指针也能对一级指针做

我们要传参给数组改变变量需要传它的地址(指针),那么我们需要改变一级指针的时候就要传一级指针的地址(二级指针),作用也就体现再了这里 

#include <stdio.h>void Swap(int** a, int** b)
{int tmp = *a;*a = *b;*b = tmp;
}int main()
{int* a = 10;int* b = 8;Swap(&a, &b);printf("a = %d, b = %d\n", a, b);return 0;
}

2. 一维数组和二维数组的本质

一维数组其实就是指针的另一种形式,二维数组也就是二级指针的另一种形式

例如:

int* a 和 int a[]
int** a 和 int a[][5]
//二维数组第二个[]必须有值

 怎么证明呢?

int main()
{int a[] = { 1,2,3,4,5 };printf("a[1] = %d, *(a + 1) = %d\n", a[1], *(a + 1));return 0;
}

 

这里的a[1] 和 *(a+1) 最终打印出来的结果是一致的 

所以为什么数组的第一个数组要从0开始而不是从1开始呢?

大概是为了契合指针的引用而做了从0开始的决定,这样a的下标是几指针加几都是一样的结果

二级指针也是这样

int main()
{int a[][5] = { 1,2,3,4,5, 1,2,3,4,5, 1,2,3,4,5 };printf("a[1][1] = %d, *(*(a + 1) + 1) = %d\n", a[1][1], *(*(a + 1) + 1));return 0;
}

甚至指针和数组结合起来一起使用也是可以的,两者并不冲突

int main()
{int a[][5] = { 1,2,3,4,5, 1,2,3,4,5, 1,2,3,4,5 };printf("*(a[1] + 1) = %d\n", *(a[1] + 1));return 0;
}

所以我们使用[]也是解引用,*也是解引用

3. 指针数组

指针数组是指针还是数组?

答案是数组,它是存放指针的数组

我们可以这么记:什么的什么,前面是形容词后面是名词,那么答案当然就是那个名词了

形似这样 

这个数组的每一个元素都是存放着一个指针的,上图存放的是一个整型指针

指针数组的每个元素又是一个个地址,又可以指向另一块区域 

int main()
{int* p1 = 1;int* p2 = 2;int* p3 = 3;int* p4 = 4;int* p5 = 5;int* arr[5] = { p1,p2,p3,p4,p5 };printf("arr[2] = %d", arr[2]);return 0;
}
输出:3

4. 数组指针

前面讲了指针数组是数组,那么数组指针当然就是指针了

让我们来睁大眼睛好好的区分一下

int *p1[10]; //指针数组
int (*p2)[10]; //数组指针

上面的指针数组里的指针没有加上小括号,所以 * 会优先和 int 结合,p1自然就和[10]结合,所以这是个有10个元素的整型指针数组 

下面的数组指针里的指针加上了小括号,所以*先和p2形成一个指针,那么这个指针会指向后面的数组,所以这是个整型的数组指针

如果我们需要存放一个数组的地址,那么当然就是存放在数组指针里了

int arr[5];
int (*p2)[10] = &arr;

5. 函数指针

函数也是有它自己的地址的

void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}int main()
{printf("Swap: %p\n", Swap);printf("&Swap: %p\n", &Swap);return 0;
}

既然函数是有地址的,那么我们未来也有可能会需要将函数的地址存储起来,所以就有了函数指针

void (*pf1)(int, int) = &Swap;
void (*pf2)(int, int)= Swap;

 上面的两种方法都是一样的,可以获取Swap的地址存储到pf1或者pf2中 

前面的返回值要和函数相同,后面的参数也要和函数相同,即使没有参数也要加个 ()

6. typedef的使用

typedef是用来对类型进行重命名的,可以将复杂的类型简单化

如果你觉得unsigned int 写起来不方便,那么我们可以用typedef对它进行重命名,那么以后就可以用uint代替unsigned int 了

typedef unsigned int uint;

自定义类型也是可以使用的,以后自己定义的结构体、枚举等都可以用这个方法重命名,让我们的代码写起来更方便,看起来更简洁

typedef int* ptr_t;
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
typedef void(*pfun_t)(int); //新的类型名必须在*的右边

 上面还有一些特殊的写法,新的名字并不是一定都是写在后面的,要注意看是什么类型才能决定怎么使用

7. 函数指针数组

跟前面的理解方法一样,函数指针数组是数组,是用一个数组存放多个函数的地址,这个数组就是函数指针数组

下面的转移表可以很好的帮助我们理解它

7.1 转移表

#include <stdio.h>int add(int a, int b)
{return a + b;
}int sub(int a, int b)
{return a - b;
}int mul(int a, int b)
{return a * b;
}int div(int a, int b)
{return a / b;
}void menu()
{printf("***********************\n");printf("***** 1.add 2.sub *****\n");printf("***** 3.mul 4.div *****\n");printf("***** 0.exit      *****\n");printf("***********************\n");
}int main()
{int x, y;int input;int (*p[5])(int, int) = { 0,add,sub,mul,div };do{menu();printf("请选择:>");scanf("%d", &input);if (input >= 1 && input <= 4){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);int ret = p[input](x, y);printf("%d\n", ret);}else if (input == 0){printf("退出计算器\n");}else{printf("输入错误,请重新输入\n");}} while (input);return 0;
}

 上面我们定义了一个p[5]数组来存放0和4个函数的地址,我们知道了它的地址就可以直接使用它

使用方法:

这里的ret是用来存放函数返回之后的结果,这里先用p[input]解引用得到函数的地址,再加上参数就可以使用那个函数了

比如 input = 1 ,那么这个p[1]存放的是add的地址,那么就相当于add(x, y),跟平常调用函数没有区别,使用函数指针数组可以让我们的代码更加简洁,如果一个一个写调用的话就比较麻烦,看起来的效果自然没有这个好

感谢观看


相关文章:

C语言:指针的进阶讲解

目录 1. 二级指针 1.1 二级指针是什么&#xff1f; 1.2 二级指针的作用 2. 一维数组和二维数组的本质 3. 指针数组 4. 数组指针 5. 函数指针 6. typedef的使用 7. 函数指针数组 7.1 转移表 1. 二级指针 如果了解了一级指针&#xff0c;那二级指针也是可以很好的理解…...

基于SSM的车位租赁系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的车位租赁系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spri…...

Java pyhon C C++ R JS 主流语言的区别-03

以下是对这几种语言的数据类型进行简要归纳&#xff1a; Java的数据类型&#xff1a; 基本数据类型&#xff1a;包括整数类型&#xff08;byte、short、int、long&#xff09;、浮点数类型&#xff08;float、double&#xff09;、字符类型&#xff08;char&#xff09;和布尔…...

5 buuctf解题

命令执行 [BJDCTF2020]EasySearch1 打开题目 尝试弱口令&#xff0c;发现没有用 扫描一下后台&#xff0c;最后用御剑扫描到了index.php.swp 访问一下得到源码 源码如下 <?phpob_start();function get_hash(){$chars ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu…...

微服务三十五关

1.微服务有什么好处&#xff1f; 微服务优点很多&#xff0c;但是我们通常说一个东西好肯定会跟另一个东西比较&#xff0c; 通常说微服务好会和单体项目进行比较。以下是微服务相对于单体项目的一些显著好处&#xff1a; 首先&#xff0c;让我们讨论单体项目的一些主要缺点&a…...

第一个 Angular 项目 - 添加服务

第一个 Angular 项目 - 添加服务 这里主要用到的内容就是 [Angular 基础] - service 服务 提到的 前置项目在 第一个 Angular 项目 - 动态页面 这里查看 想要实现的功能是简化 shopping-list 和 recipe 之间的跨组件交流 回顾一下项目的结构&#xff1a; ❯ tree src/app/…...

红日靶场3

靶场链接&#xff1a;漏洞详情 在虚拟机的网络编辑器中添加两个仅主机网卡 信息搜集 端口扫描 外网机处于网端192.168.1.0/24中&#xff0c;扫描外网IP端口&#xff0c;开放了80 22 3306端口 80端口http服务&#xff0c;可以尝试登录网页 3306端口mysql服务&#xff0c;可…...

B树的介绍

R-B Tree 简介特性B树特性m阶B树的性质&#xff08;这些性质是B树规定的&#xff09; B树的搜索B树的添加B树的删除——非叶子结点 简介 R-B Tree又称为Red-Black Tree&#xff0c;红黑树。是一种特殊的二叉查找树&#xff0c;红黑树的每个节点上都有存储为表示结点的颜色&…...

《The Art of InnoDB》第二部分|第4章:深入结构-磁盘结构-撕裂的页面(doublewrite buffer)

4.5 撕裂的页面 目录 4.5 撕裂的页面 4.5.1 双写缓冲区的作用 4.5.2 双写缓冲区的结构 4.5.3 双写缓冲区与Redolog的协同工作流程 4.5.2 双写缓冲区写入时机 4.5.3 禁用双写缓冲区 4.5.4 小结 未完待续... 上文我们学习了redo log的结构和其工作原理,它是一个…...

提示工程(Prompt Engineering)、微调(Fine-tuning) 和 嵌入(Embedding)

主要参考资料&#xff1a; 还没搞懂嵌入&#xff08;Embedding&#xff09;、微调&#xff08;Fine-tuning&#xff09;和提示工程&#xff08;Prompt Engineering&#xff09;&#xff1f;: https://blog.csdn.net/DynmicResource/article/details/133638079 B站Up主Nenly同学…...

【Flink精讲】Flink 内存管理

面临的问题 目前&#xff0c; 大数据计算引擎主要用 Java 或是基于 JVM 的编程语言实现的&#xff0c;例如 Apache Hadoop、 Apache Spark、 Apache Drill、 Apache Flink 等。 Java 语言的好处在于程序员不需要太关注底层内存资源的管理&#xff0c;但同样会面临一个问题&…...

正则化概念及使用

正则化概念及使用 正则化概念正则化原理常用的两种正则化方法1. L1 正则化&#xff08;Lasso&#xff09;2. L2 正则化&#xff08;Ridge&#xff09; 正则化参数 正则化概念 在机器学习中&#xff0c;我们致力于通过从训练数据中学习模式或规律来构建模型。为了找到最佳的模型…...

让程序员设计B端界面,好比武大郎招聘:向我看齐。不忍直视!

hello&#xff0c;我是大美B端工场&#xff0c;B端系统的要求越来越高了&#xff0c;很多公司还让程序员负责页面&#xff0c;页面搞的没法看&#xff0c;也怪不得程序员。程序员来搞页面&#xff0c;那还不是武大郎招聘——向我看齐&#xff0c;以我的标准为标准吗&#xff1f…...

使用python构建Android,探索跨平台应用开发Kivy框架

使用python构建Android&#xff0c;探索跨平台应用开发Kivy框架 1. 介绍Kivy框架 Kivy是什么&#xff1f; Kivy是一个开源的Python跨平台应用程序开发框架&#xff0c;旨在帮助开发者快速构建创新的、可扩展的移动应用和多点触控应用。Kivy采用MIT许可证&#xff0c;允许开发…...

08 Redis之集群的搭建和复制原理+哨兵机制+CAP定理+Raft算法

5 Redis 集群 2.8版本之前, Redis采用主从集群模式. 实现了数据备份和读写分离 2.8版本之后, Redis采用Sentinel哨兵集群模式 , 实现了集群的高可用 5.1 主从集群搭建 首先, 基本所有系统 , “读” 的压力都大于 “写” 的压力 Redis 的主从集群是一个“一主多从”的读写分…...

*MYSQL--索引--内部原理

MYSQL的索引根据功能,主要有三大类型: 1.HASH索引 2.二叉树 3.BTREE索引 一:HASH索引 1.内部原理: 在设置了某列为索引列之后,并且开始或者将要在相应索引列创建数据的时候,系统通过某种算法 F(X) 自动计算出来一个十六进制的哈希值,这个哈希值能够对应相应的字段值 所以…...

docker安装kafka和kafka-console-ui

3、安装kafka https://blog.csdn.net/m0_64210833/article/details/134199061 kafka依赖Zookeeper,当然也可以用内置的kraft。 安装前提条件 1.安装Zookeeper 1.1运行ZooKeeper容器 2.运行Kafka容器 2.1启动Kafka容器 3.验证 3.1进入Kafka容器 3.2查看容器状态 3.3查…...

Linux:gitlab创建组,创建用户,创建项目

创建组和项目 让后可以在组里创建一个个仓库 创建成员 我创建个成员再把他分配进这个组里 进入管理员 密码等会我们创建完用户再去配置密码 Regular是普通的用户&#xff0c;只可以正常去访问指定规则的项目 而下面的administrator就是管理员&#xff0c;可以随便进项目&…...

相机选型介绍

摄影测量中&#xff0c;相机是非常重要的角色&#xff0c;合适的相机产出合适的图像&#xff0c;得到合适的重建精度&#xff0c;这是相机的重要性。 您也许第一反应是&#xff0c;摄影测量所需的理想相机&#xff0c;是有着超高分辨率的相机&#xff0c;但事实可能并非如此&a…...

SQL创建数据库

SQL&#xff0c;全称结构化查询语言(Structured Query Language)&#xff0c;是一种用于管理关系型数据库的标准语言。通过 SQL&#xff0c;我们可以创建、查询、更新和删除数据库中的数据。今天&#xff0c;我们将学习使用SQL创建数据库。本文的目标是让读者了解如何使用SQL创…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

Java设计模式:责任链模式

一、什么是责任链模式&#xff1f; 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09; 是一种 行为型设计模式&#xff0c;它通过将请求沿着一条处理链传递&#xff0c;直到某个对象处理它为止。这种模式的核心思想是 解耦请求的发送者和接收者&#xff0c;…...

AT模式下的全局锁冲突如何解决?

一、全局锁冲突解决方案 1. 业务层重试机制&#xff08;推荐方案&#xff09; Service public class OrderService {GlobalTransactionalRetryable(maxAttempts 3, backoff Backoff(delay 100))public void createOrder(OrderDTO order) {// 库存扣减&#xff08;自动加全…...

Spring AI中使用ChatMemory实现会话记忆功能

文章目录 1、需求2、ChatMemory中消息的存储位置3、实现步骤1、引入依赖2、配置Spring AI3、配置chatmemory4、java层传递conversaionId 4、验证5、完整代码6、参考文档 1、需求 我们知道大型语言模型 &#xff08;LLM&#xff09; 是无状态的&#xff0c;这就意味着他们不会保…...

KKCMS部署

目录 账号 网站目录 快看CMS使用手册 http://10.141.19.241/kkcms/install/ 常规思路&#xff1a;页面点点观察url变化&#xff0c;参数 常规思路&#xff1a;点一个功能模块抓包看什么东西&#xff0c;正确是什么样&#xff0c;错误的是什么样&#xff0c;构造参数。 账号…...