C语言-指针作为函数返回值及二级指针

news2024/11/23 7:21:06

1、指针作为函数返回值

c语言允许函数的返回值是一个指针(地址)我们将这样的函数称为指针函数,下面的例子定义一了一个函数strlong(),用来返回两个字符串中较长的一个:

1. #include <stdio.h>
2. #include <string.h>
3.
4. char *strlong(char *str1, char *str2){
5. if(strlen(str1) >= strlen(str2)){
6. return str1;
7. }else{
8. return str2;
9. }
10. }
11.
12. int main(){
13. char str1[30], str2[30], *str;
14. gets(str1);
15. gets(str2);
16. str = strlong(str1, str2);
17. printf("Longer string: %s\\n", str);
18.
19. return 0;
20. }
运行结果:
C Language↙
c.biancheng.net↙
Longer string: [c.biancheng.net](<http://c.biancheng.net/>)

用指针函数作为函数返回值时需要注意的一点时,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回指针请尽量不要指向这些数据,c语言没有任何机制来保证这些数据会一直有效,它们在后续使用过程中可能会引发运行时错误,请看下面的例子:

1. #include <stdio.h>
2.
3. int *func(){
4. int n = 100;
5. return &n;
6. }
7.
8. int main(){
9. int *p = func(), n;
10. n = *p;
11. printf("value = %d\\n", n);
12. return 0;
13. }
运行结果:
value = 100

n是func()内部的局部变量,func()反悔了指向n的指针,根据上面的观点,func()运行结束后n被销毁,是用p应该获取不到n的值,但是从运行结果来看,我们的推理好像是错误的,func()运行结束后p依然可以获取局部变量n的值,这个上面的观点不是相对的吗?

为了进一步看清问题的本质,不放将上面的代码稍作修改,在第9-10行之间增加一个函数调用,看看会有什么效果:

1. #include <stdio.h>
2.
3. int *func(){
4. int n = 100;
5. return &n;
6. }
7.
8. int main(){
9. int *p = func(), n;
10. printf("c.biancheng.net\\n");
11. n = *p;
12. printf("value = %d\\n", n);
13. return 0;
14. }
运行结果:
[c.biancheng.net](<http://c.biancheng.net/>)
value = -2

可以看到,现在p指向的数据已经不是原来n的值了,它编程了一个毫无意义的甚至有些怪异的值。与前面的代码相比,该段代码仅仅是在*p之前增加了一个函数调用,这一细节的不同却导致运行结果有天壤之别,究竟是为什么呢?

前面我们说函数运行结束后会销毁所有的局部数据,这个观点并没错,大部分 C 语言教材也都强调了这一点。但是,这里所谓的销毁并不是将局部数据所占用的内存全部抹掉,而是程序放弃对它的使用权限,弃之不理,后面的代码可以随意使用这块内存。对于上面的两个例子,func() 运行结束后 n 的内存依然保持原样,值还是 100,如果使用及时也能够得到正确的数据,如果有其它函数被调用就会覆盖这块内存,得到的数据就失去了意义。

第一个例子在调用其他函数之前使用 *p 抢先获得了 n 的值并将它保存起来,第二个例子显然没有抓住机会,有其他函数被调用后才使用 *p 获取数据,这个时候已经晚了,内存已经被后来的函数覆盖了,而覆盖它的究竟是一份什么样的数据我们无从推断(一般是一个没有意义甚至有些怪异的值)。

2、二级指针

 

指针可以指向一份普通类型的数据,例如int、double、char等,也可以指向一份指针类型的数据,例如int *、doubule *、char *等。

如果一个指针指向的是另一个指针,我们就称它为二级指针,或者指向指针的指针。

假设有一个int类型的变量a,p1是指向a的指针变量,p2又是指向p1的指针变量,他们的关系如下图:

将这种关系转换为c语言代码:

1. int a =100;
2. int *p1 = &a;
3. int **p2 = &p1;

指针变量也是一种变量,也会占用存储空间、也可以使用&获取它的地址。c语言不限制指针的级数,每增加一级指针,在定义指针变量是就得增加一个星号*。p1是一级指针,指向普通类型的数据,定义时只有一个*;p2是二级指针,指向一级指针p1,定义时有两个*。

如果我们希望在定义一个三级指针p3,让它指向p2,那么可以这样写:

int ***p3 = &p2;

四级指针也是类似的道理:

int ****p4 = &p3;

实际开发中会经常使用一级指针和二级指针,几乎用不到高级指针。

想要获取指针指向的数据是,一级指针加一个*,二级指针加两个*,三级指针加三个*,以此类推,请看代码:

1. #include <stdio.h>
2.
3. int main(){
4. int a =100;
5. int *p1 = &a;
6. int **p2 = &p1;
7. int ***p3 = &p2;
8.
9. printf("%d, %d, %d, %d\\n", a, *p1, **p2, ***p3);
10. printf("&p2 = %#X, p3 = %#X\\n", &p2, p3);
11. printf("&p1 = %#X, p2 = %#X, *p3 = %#X\\n", &p1, p2, *p3);
12. printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\\n", &a, p1, *p2, **p3);
13. return 0;
14. }

运行结果:

100, 100, 100, 100
&p2 = 0X28FF3C, p3 = 0X28FF3C
&p1 = 0X28FF40, p2 = 0X28FF40, *p3 = 0X28FF40
&a = 0X28FF44, p1 = 0X28FF44, *p2 = 0X28FF44, **p3 = 0X28FF44

以三级指针p3为例来分析上面的代码,*p3等价于p3))。p3得到的是p2的值,也即使p1的地址;p3)得到的是p1的值,也即是a的地址,经过三次“取值”操作后,(*p3))得到的才是a的值。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实时数据开发 | 怎么通俗理解Flink容错机制,提到的checkpoint、barrier、Savepoint、sink都是什么

今天学Flink的关键技术–容错机制&#xff0c;用一些通俗的比喻来讲这个复杂的过程。参考自《离线和实时大数据开发实战》 需要先回顾昨天发的Flink关键概念 检查点&#xff08;checkpoint&#xff09; Flink容错机制的核心是分布式数据流和状态的快照&#xff0c;从而当分布…

再次讨论下孤注一掷

在孤注一掷中的黑客技术里面&#xff0c;简单介绍了电影孤注一掷中用的一些"黑科技"&#xff0c;这里继续讨论下&#xff0c;抛弃这些黑科技&#xff0c;即使在绝对公平的情况下&#xff0c;你也一样赢不了赌场 相对论有一个假设就是光速不变&#xff0c;这里也有个…

微信小程序技术架构图

一、视图层1.WXML&#xff08;WeiXin Markup Language&#xff09; 这是微信小程序的标记语言&#xff0c;类似于 HTML。它用于构建小程序的页面结构。例如&#xff0c;通过标签来定义各种视图元素&#xff0c;如<view>&#xff08;类似于 HTML 中的<div>&#xff…

GaussDB 华为高斯数据库

GaussDB 是华为推出的一款企业级分布式数据库&#xff0c;旨在为企业提供高效、可靠、安全的数据库服务。GaussDB 基于华为在数据库领域的多年积累&#xff0c;结合人工智能技术和分布式架构&#xff0c;支持多种场景的数据存储与管理需求&#xff0c;是云计算、大数据、人工智…

redis工程实战介绍(含面试题)

文章目录 redis单线程VS多线程面试题**redis是多线程还是单线程,为什么是单线程****聊聊redis的多线程特性和IO多路复用****io多路复用模型****redis如此快的原因** BigKey大批量插入数据测试数据key面试题海量数据里查询某一固定前缀的key如果生产上限值keys * &#xff0c;fl…

C++从零到满绩——入门基础and类和对象(上)

目录 1>>前言 2>>函数重载 3>>引用 3.1>>引用的概念 3.2>>引用三大特性 3.3>>引用的使用 3.4>>const引用 3.5>>指针与引用的关系 4>>inline内联函数 5>>nullptr 6>>类和对象&#xff08;上&#…

DDPM与DDIM中的采样

在深度生成模型中&#xff0c;采样&#xff08;Sampling&#xff09;指的是根据模型生成新样本的过程。在扩散模型&#xff08;Diffusion Models&#xff09;中&#xff0c;采样的关键是从高斯噪声逐步还原出原始数据。让我们分别探讨 DDPM 和 DDIM 的采样过程&#xff0c;以及…

python oa服务器巡检报告脚本的重构和修改(适应数盾OTP)有空再去改

Two-Step Vertification required&#xff1a; Please enter the mobile app OTPverification code: 01.因为巡检的服务器要双因子认证登录&#xff0c;也就是登录堡垒机时还要输入验证码。这对我的巡检查服务器的工作带来了不便。它的机制是每一次登录&#xff0c;算一次会话…

【Web前端】创建我的第一个 Web 表单

Web 开发中&#xff0c;表单是不可或缺的组成部分。无论是用户注册、登录还是反馈收集&#xff0c;表单都是与用户交互的重要方式。 什么是 Web 表单&#xff1f; Web 表单是一种用于收集用户输入数据的界面元素。它们允许用户在浏览器中输入信息并提交这些信息到服务器。Web …

JavaWeb后端开发知识储备2

目录 1.HttpClient 2.微信小程序开发 3.Spring Cache 1.HttpClient 简单来说&#xff0c;HttpClient可以通过编码的方式在Java中发送Http请求 2.微信小程序开发 微信小程序的开发本质上是前端开发&#xff0c;对于后端程序员来说了解即可 3.Spring Cache Spring Cache 是…

力扣刷题--21.合并两个有序链表

I am the best &#xff01;&#xff01;&#xff01; 题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2…

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现

开题报告 随着旅游业的快速发展和互联网的普及&#xff0c;越来越多的人选择通过网络平台获取旅游攻略和分享旅行经验。传统的旅游攻略获取方式往往依赖于纸质书籍或零散的在线资源&#xff0c;这种方式不仅信息更新滞后&#xff0c;而且缺乏互动性和个性化推荐。因此&#xf…

排序算法:直接插入排序,希尔排序,选择排序,快速排序,堆排序,归并排序

1.直接插入排序 基本思想&#xff1a;把待排序的数按照大小逐个插入到前面已经排序好的有序序列中&#xff0c;直到所有的都插入完为止&#xff0c;得到一个新的有序序列。 如图所示&#xff0c;当插入第i个&#xff08;i>1&#xff09;元素的时候&#xff0c;前面的arr[0]…

《OpenCV 图像基础操作全解析:从读取到像素处理与 ROI 应用》

简介&#xff1a;本文详细介绍了使用 OpenCV 进行图像相关操作的基础知识与实践示例&#xff0c;涵盖图像读取&#xff08;包括不同读取方式及对应效果&#xff09;、灰度值概念与图像矩阵存储特点、通道相关知识&#xff08;如 BGR、通道拆分与合并&#xff09;&#xff0c;还…

使用卡尔曼滤波器估计pybullet中的机器人位置

⭐️ 卡尔曼滤波 卡尔曼滤波是一种递归算法&#xff0c;用于从具有噪声的观测中估计系统状态。它特别适合用于线性、高斯动态系统。 笔者之前写过一篇博文介绍卡尔曼滤波器《boss:整个卡尔曼滤波器的简单案例——估计机器人位置》&#xff0c;本文手动实现一个卡尔曼滤波器并…

【尚筹网】二、环境搭建一

【尚筹网】二、环境搭建一 环境搭建总体目标创建工程系统架构图工程创建计划创建空项目创建对应的 Maven 模块建立模块间的依赖 创建数据库基于 Maven 的 Mybatis 的逆向过程配置 pom创建 generatorConfig.xml执行逆向工程操作的 maven 指令将逆向工程生成的资源归位 父工程依赖…

全面解析 JMeter 后置处理器:概念、工作原理与应用场景

在性能测试中&#xff0c;Apache JMeter是一个非常流行的工具&#xff0c;它不仅能够模拟大量用户进行并发访问&#xff0c;还提供了丰富的扩展机制来满足各种复杂的测试需求。后置处理器&#xff08;Post-Processor&#xff09;是JMeter中非常重要的组件之一&#xff0c;用于在…

数字IC后端实现时钟树综合系列教程 | Clock Tree,Clock Skew Group之间的区别和联系

Q: Clock&#xff0c;Clock Tree和Skew Group有何区别&#xff1f;Innovus CCOPT引擎是如何使用这些的&#xff1f; Clock是时序约束SDC中的时钟定义点。 create_clock -name clk_osc -period $period_24m [get_ports xin_osc0_func] 时钟树综合(Clock Tree Synthesis)之前应…

基于零相差前馈补偿的 PID 控制

零相差前馈补偿是一种结合前馈补偿与反馈控制的策略&#xff0c;旨在提高控制系统对参考信号的跟踪精度。通过设计合理的前馈补偿器&#xff0c;使得系统对参考输入实现零相位差的跟踪&#xff0c;同时利用 PID 控制器保证系统的稳定性和动态性能。 1. 原理概述 目标&#xff…

odoo18中模型的常用字段类型

字段的公共属性: Char 字符类型&#xff0c;对应数据库中varchar类型&#xff0c;除了通用类型外接收另外两个参数&#xff1a; size: 字符长度&#xff0c;超出的长度将被截断 trim: 默认True&#xff0c;是否字段值应该被去空白。 Text 文本类型&#xff0c;对应数据库…