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

【c语言】指针3

1、字符指针变量

        指针类型中我们知道有一种为字符指针char*的指针类型,其使用方法如下:

        

          上面我们是先将字符使用一个变量,然后将变量的地址传给一个字符指针变量,通过指针变             量实现了对这个字符的打印。还有下面的这种方法:

   

             上述的代码很很多人可能会认为是将字符串hello word放在了字符指针ps中了,其实不                   然,其本质上是将字符串的首字符的地址放在了ps指针中,如下图所示:

   

               下面我们看一道《剑指offer》中的一道和字符串有关的笔试题:

     

              我们来对这个代码进行一个分析:

              这里的str3和str4指向的是同一个常量字符串。在c\c++中会把常量字符串存储到一个单独                的内存区域,当几个指针变量去指向同一个字符串常量的时候,他们实际上会指向同一                  块内存。但是用相同的常量字符串的初始化不同数组的时候就会开辟出不同的内存块。                  所以str1和str2的地址是不同的,而str3和str4的地址是一样的。

              所以其输出的结果如下:

           

                

 2、数组指针变量 


1、数组指针的定义         

         前面我们学习了,指针数组,指针数组是一种数组,数组中存放的是地址(指针)

         那么数组指针是什么呢?是指针变量还是数组呢?

         其是指针变量。

         前面我们已经熟悉了:

         整型指针变量:存放的是整型变量的地址,能够指向整型数据的指针。

         浮点型指针变量:存放的是浮点型变量的地址,能够指向浮点型数据的指针。

         那么数组指针就应该是存放数组的地址的指针,能够指向数组的指针。

         那么数组指针的写法是咋样的呢?

         我们看下面两个写法:

         int *p1[10];

         int (*p2)[10];

         这里我们思考一下p1和p2都是数组指针吗?

         我们先来看第一个、这里的*号会和前面的int结合,因为[]操作符的优先级是比*号要高的,             那么就导致了p1是和后面的[10]结合的,*和int结合,那么就是一个指针数组。

         而第二个的话,p2和*是用小括号括在一起的,那么就改变了整个表达式的优先级,此时的*           是和p2结合的,那么此时的p2是一个数组指针,其指向的是一个数组。

         所以数组指针变量的定义方法如下:

         int (*p)[10];

2、数组指针的初始化

         数组指针变量是用来存放数组的地址的,那么我们可以使用前面所学习的取地址符号&来取             数组的地址,如下:

         

        整个表达式的具体情况如下:

             

        前面我们也对数组的取地址进行了详细的说明了,如果有不懂的同学可以看前面的内容。

3、二维数组传参的本质 

        前面我们有了数组指针变量的的理解,我们就来学习一个二维数组传参的本质。

        过去我们需要二维数组做为参数的时候是下面这样写的:

        

             如上我们要定义一个函数,要实现的功能是将二维数组成行的打印出来。那么要将二维数               组作为参数,其此形参和实参都写成了二维数组的形式,那么我们还有其它的写法吗?

             我们再对二维数组进行理解,二维数组可以看成一维数组,其元素是一维数组。我们在传               参的时候都会传这二维数组的数组名,我们也都知道一个数组的数组名是首元素的地址,               那么二维数组又可以看成是 一维数组的数组,那么此时二维数组的首元素的地址是什么                 呢?

          

              根据数组名就是首元素的地址这个规则,那么二维数组的数组名就是其第一行元素的地                  址,是一维数组的地址。那么根据我们上面的例子,第一行的一维数组就是int[5],所以                  第一行的地址就是数组指针类型int (*)[5],那么也就是说二维数组传参的本质也是传地                    址,传递的是二维数组第一行数组的地址,那么我们形参可以写成数组指针的形式来接                  收这个地址。如下所示:

     

              运行结果如下:

             可以看到我们使用数组指针的方式来接收这二维数组也可以将这个数组完全打印出来

             我们上面的代码,只是将形参改变了,我们是否可以使用解引用的方式就数组的元素进行               打印呢?下面我们来分析一下:

             前面我们说到,二维数组可以看成元素是一维数组的一维数组, 那么这个一维数组名字                 是啥呢?

             第一行的数组名:arr[0], 同理第二行数组名:arr[1],第三行数组名:arr[2]。

             那么*(p+0),*(p+1),*(p+2),分别就是第一行第一个元素,第二行第一个元素,第三行               第一个元素,即:*(p+0)=arr[0],*(p+1)=arr[1],*(p+2)=arr[2]。

             那么就是每一行的首元素地址我们就都有了,那么我们再对其进行+1操作就可以访问到其               一行上的元素了。

            例如:*(*(p+0)+ 1)就是第一行第二个元素了。

            下面我们通过代码来看看:

            

               运行结果如下:

  

         综上:二维数组传参,形参的部分可以写成二维数组,也可以写成指针形式,因为数                                 组传参的本质还是地址传递  。

4、函数指针变量

1、函数指针变量的创建

      什么是函数指针变量呢?

      在前面我们学习了整型指针,数组指针的时候,我们举一反三,不难得到函数指针就是用来存        放函数地址的指针变量,未来我们需要使用这个函数可以通过这个指针来调用这个函数的。

      那么函数是否也是有地址的呢?下面我们通过代码来验证一下:

         运行结果:

  

         可以看到确实将函数的地址打印出来了,而且我们还发现函数名就是函数的地址,当然也可           以通过&函数名的方式取得函数的地址。

         如果我们要将函数的地址存储起来,那么我们可以通过函数指针来存放,函数指针的写法和           整型指针的写法类似。

        下面我们通过代码的方式来理解:

       

         首先就是指针的类型是要和函数的返回值类型一样的,例如上面的返回值为整型那么函数指           针的类型也是为整型,如果无返回值,那么函数指针的类型也会无返回值类型void。然后就           是那个*     符号,应该和名字一起放在一个小括号内。

         然后就是对于无参数的函数,其定义指针变量的时候,就没什么特别要求了。

         有参数的函数,对于接收其地址的指针变量的定义,则也要和函数一样,将其参数写出来。

        下图是关于函数指针的解析:

    

2、函数指针的使用            

      可以通过函数指针来实现函数的调用,由于函数名就是函数的地址,所以我们在使用时,可以        直接使用函数指针变量替换函数名。

      下面我们通过一个例子来理解:

      

        输出结果:

  

            我们还可以对p进行解引用,这样也可以拿到函数add的地址。

   

          运行结果:

 

3、两段有趣的代码   

     代码1:

      

      这个代码我们一眼看去就会很懵逼,不知道从何下手。我们可以从其 特殊的部分出发,这段          代码中最特殊的就是那个0了,我们从0将这个代码分开来看。 

      其左边部分为:(void(*)())

      很明显这是一个函数指针类型,其放在0的前面的作用是什么呢?

       这是一个强制类型转换,例如:(int)3.14;在这个浮点数前面加上一个括号,括号里面加上           需要转换的数据类型,就可以将这个数据转换为括号内的类型了。

       那么上述的代码也就是将这个0强制转换为一个函数指针类型,那么此时的0就是一个指针,           然后前面的*号就是对其解引用,后面的小括号就是函数本身。 

       综上所述:上述代码其实就是一个函数。只是这个函数没有参数。

       代码2:

       void ( * signal ( int  , void ( * ) ( int ) ) )( int ) ;

       我们首先从signal入手,这个看起来和一个函数声明一样,他的参数分别是一个整型数据,还         有一个函数指针。

       那么剩下的是什么呢?

       我们将中间已经解析的部分去掉:、

       void(*)(int);

       很明显这个部分也是一个函数指针,其实这是signal这个函数的返回类型,那么其为何不和我         们前面学习到的函数定义那个部分那样去写呢:void (*) (int) sighnal(int,void(*)(int));

       这样似乎更加容易读懂。但是在c语言中未定义的,所以还是要使用一开始定义方式,不可以         使用修改的那个定义方式。

       综上所述:这段代码是对函数sighnal的声明,他是参数为一个整型数据,一个函数指针,                                其返回类型会函数指针。

5、typedef关键字

         typedef就是type   define,他是用来对类型重命名的,可以将复杂的类型简单化。

         比如说,我们觉得unsigned  int 这个类型写的话不是很方便,那么我们使用typedef关键字对           其重命名,命名成自己觉得合适的名字。下面我们来演示一遍给你看看。

         

        这个关键字对于指针类型也是适用的:

        写法和上面是一样的:typedef  int*   prt_t;

        上述的代码我们就将整型指针类型重命名为prt_t了。

        不过对于数组指针和函数指针的重命名就有一些差异了。

        例如我们现在有一个数组指针类型int (*)[5];需要将其重命名为arr;写法如下:

        typedef  int(*arr) [5]; 

        对于函数指针void(*)(int)的重命名方式如下:

       typedef void(*pri_t)(int) ;

       那么我们在前面看到的有趣的代码2中我们可以进行一个优化使得其易懂一些:

       typedef  void (*pri_t)(int);

       pri_t  signal(int , pri_t);

       这样就更加容易读懂了。

6、函数指针数组       

        数组是存放相同类型数据的存储空间,前面我们已经学习了指针数组,是用来存放指针的。

        那么函数指针数组顾名思义也就是用来存放函数指针的。

       那么函数指针数组是如何定义呢?

       下面有三种定义方式,我们看看那种是正确的。

       

       可以看到编译器就已经对第二第三种报错了,这是为什么呢?

       parr1先和[]结合,说明parr1是数组,数组存放的数据类型是什么?就是int (*)()类型,是存放           返回值类型为整型的函数的地址。 

       下面我们通过一个实例来感受函数指针数组的作用。

7、转移表

        函数指针的用途:转移表

        例:计算器的实现,要求可以使用户通过选择,是实现简单的加减乘除的运算。

        前面我们写过一个函数是实现求加法运算的。下面我们将其他几个功能也实现。

        计算器的实现:

        下面为简单的方法:

        

          

        通过上面的代码我们是实现了这些功能,但是我们在编写的过程可以看到代码中有大量的重            复,那么我们可不可以优化一下呢?

        我们发现这些函数的返回类型和参数的个数和类型都是一样的。那么我们可以创建一个函数            指针数组。因为要实现四个功能,这里我们在数组的创建有个技巧,就是将下标为0的用一个          0占用,然后这样我们的函数的下标就从1开始了 。

        优化后的代码:

        

         这样我们看到主函数中的代码整洁多了,代码的可读性也高了。  

        

            

相关文章:

【c语言】指针3

1、字符指针变量 指针类型中我们知道有一种为字符指针char*的指针类型,其使用方法如下: 上面我们是先将字符使用一个变量,然后将变量的地址传给一个字符指针变量,通过指针变 量实现了对这个字符的打印。还有下面的这种…...

【开源】A063—基于Spring Boot的农产品直卖平台的设计与实现

🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...

Can‘t find variable: token(token is not defined)

文章目录 例子 1:使用 var例子 2:使用 let 或 const例子 3:异步操作你的代码中的情况 Cant find variable: tokentoken is not defined源代码 // index.jsPage({data: {products:[],cardLayout: grid, // 默认卡片布局为网格模式isGrid: tr…...

【JavaEE 初阶】⽹络编程套接字

一、⽹络编程基础 1.应用层 操作系统提供的一组 api >socket api(传输层给应用层提供) 2.传输层 两个核心协议. TCPUDP 差别非常大,编写代码的时候,也是不同的风格 因此, socket api 提供了两套 TCP 有连接, 可靠传输, 面向字节流, 全双工 UDP …...

【Linux内核】Hello word程序

创建测试目录 mkdir -p ~/develop/kernel/hello-1 cd ~/develop/kernel/hello-1 创建MakeFile文件和内核.c文件 nano Makefile nano hello-1.c 编写内容 /* * hello-1.c - The simplest kernel module. */ #include <linux/module.h> /* Needed by all modules */…...

PHP 与 MySQL 搭配的优势

一、PHP 与 MySQL 搭配的优势 强大的动态网页开发能力 PHP 是一种服务器端脚本语言&#xff0c;能够生成动态网页内容。它可以根据用户的请求、数据库中的数据等因素&#xff0c;实时地生成 HTML 页面返回给客户端浏览器。而 MySQL 是一个流行的关系型数据库管理系统&#xf…...

深入浅出:PHP中的变量与常量全解析

文章目录 引言理解变量普通变量赋值操作变量间赋值引用赋值取消引用 可变变量预定义变量 理解常量声明常量使用define()函数const关键字 使用常量预定义常量 扩展话题&#xff1a;作用域与生命周期实战案例总结与展望参考资料 引言 在编程的世界里&#xff0c;变量和常量是两种…...

初步简单的理解什么是库,什么是静态库,什么是动态库

库是什么 库根据名字我们应该很容易理解&#xff0c;在我们日常生活种&#xff0c;包含库的东西有很多&#xff0c;像仓库&#xff0c;库房那些&#xff0c;库是拿来存放&#xff0c;方便管理东西的&#xff0c;在我们编程当中&#xff0c;库的定义也是如此 那么为什么要有库…...

从ctfwiki开始的pwn之旅 3.ret2syscall

ret2syscall 原理 ret2syscall&#xff0c;即控制程序执行系统调用&#xff0c;获取 shell。 那么ret2text——程序中有system("/bin/sh")代码段&#xff0c;控制流执行 那么ret2shellcode——程序中不存在system("/bin/sh/")的代码段&#xff0c;自己…...

使用 httputils + protostuff 实现高性能 rpc

1、先讲讲 protostuf protostuf 一直是高性能序列化的代表之一。但是用起来&#xff0c;可难受了&#xff0c;你得先申明 protostuf 配置文件&#xff0c;并且要把这个配置文件转成类。所以必然要学习新语法、新工具。 可能真的太难受了&#xff01;于是乎&#xff0c;&#…...

系统思考—战略共识

最近与和一位企业创始人深度交流时&#xff0c;他告诉我&#xff1a;“虽然公司在制定战略时总是非常明确&#xff0c;但在执行过程中&#xff0c;经常发现不同层级对战略的理解偏差&#xff0c;甚至部分团队的执行效果与预期大相径庭。每次开会讨论时&#xff0c;大家都说得头…...

Java版-速通数据结构-树基础知识

现在面试问mysql,红黑树好像都是必备问题了。动不动就让手写红黑树或者简单介绍下红黑树。然而&#xff0c;我们如果直接去看红黑树&#xff0c;可能会一下子蒙了。在看红黑树之前&#xff0c;需要先了解下树的基础知识&#xff0c;从简单到复杂&#xff0c;看看红黑树是在什么…...

详尽的oracle sql函数

1&#xff0c;CHR 输入整数&#xff0c;返回对应字符。 用法&#xff1a;select chr(65),chr(78) from dual; 2&#xff0c;ASCII 输入字符&#xff0c;返回对应ASCII码。 用法&#xff1a;select ascii(A),ascii(B) from dual; 3&#xff0c;CONCAT 输入两个字符串&#xff0c…...

SAP IDOC Error VG205

今天在做IDOC 入栈处理销售订单的时候&#xff0c;一直报错VG205 There is no article description for item 000030 这个问题在通过WE19 前台显示的时候就不会遇见&#xff0c; 只有在接口传输的时候才会遇到 搜索发现&#xff0c;可以通过配置忽略此消息号 配置路径如下…...

DSP 的 CV 算子调用

01 前言 DSP 是 征程 5 上的数字信号处理器&#xff0c;专用于处理视觉、图像等信息。在 OE 包的 ddk/samples/vdsp_rpc_sample 路径下&#xff0c;提供了 DSP 使用示例&#xff0c;包括 nn 和 CV 两部分。 nn 示例涵盖了深度学习模型的相关算子&#xff0c;包括量化、反量化、…...

WMI攻击-基础篇(一)

#WMI攻击-基础篇&#xff08;一&#xff09; 这篇文章是关于WMI攻击系列文章的第一部分&#xff0c;面向新手。如果对Powershell有一定了解会对阅读本文有所帮助&#xff0c;但这并不是必需的&#xff0c;我们直接上干货。 #1、概述 为什么是WMI&#xff1f; WMI 是 Microso…...

使用Pygame创建一个简单的消消乐游戏

消消乐游戏是一种经典的益智游戏&#xff0c;玩家通过交换相邻的方块来形成三个或更多相同颜色的连续方块&#xff0c;从而消除它们。本文将介绍如何使用Python的Pygame库来创建一个简单的消消乐游戏。 准备工作 在开始之前&#xff0c;请确保已安装Pygame库。可以通过以下命…...

证明直纹面是可展曲面沿着直母线,曲面的切平面不变

目录 证明直纹面是可展曲面的当且仅当沿着直母线&#xff0c;曲面的切平面不变 证明直纹面是可展曲面的当且仅当沿着直母线&#xff0c;曲面的切平面不变 直纹面是可展曲面当且仅当沿着直母线&#xff0c;曲面的切平面不变. 证明&#xff1a;设直纹面 S S S的参数式为 r ( u …...

Chrome控制台 网站性能优化指标一览

打开chrome-》f12/右键查看元素-》NetWrok/网络 ctrlF5 刷新网页&#xff0c;可以看到从输入url到页面资源请求并加载网页&#xff0c;用于查看资源加载&#xff0c;接口请求&#xff0c;评估网页、网站性能等&#xff0c;如下图&#xff1a; request、stransferred、resour…...

Typora创建markdwon文件的基础语法

标题的创建 使用#空格xxx 可使xxx为标题&#xff0c;同时第一标题为#空格标题&#xff1b;第二标题为##空格标题2。以此类推最多可创建六个标题。 同时按住Ctrl1可创建第一标题&#xff0c;同时按住Ctrl2可创建第二标题&#xff0c;以此类推&#xff0c;最多可创建六个标题。也…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...