当函数参数为一级指针,二级指针

当函数参数为一级指针,二级指针

在讲述内容之前,先讲四点重要知识

1.当传入参数时,函数形参会立即申请形参的内存空间,函数执行完毕后,形参的内存空间立即释放掉

1.指针是存放其他变量地址的变量。指针有自己的内存空间,内存空间存放的是其他变量的地址。

2.指针具体指向谁,要看指针内存空间存放的地址。存放谁的地址就指向谁。

3.函数参数传参本质:实参传给形参,都是拷贝.

指针做函数参数的本质剖析,讲述四种情况

一、传变量

#include <iostream>
using namespace std;
#include <cstring>
using namespace std;
void change(int value)
{cout<<"change:: "<<&value<<endl;value = 20;
}int main()
{int a = 10;cout<<"main:: "<<&a<<endl;change(a);cout << a << endl;return 0;
}

在这里插入图片描述

为什么a的值没发生变化,请从地址空间的角度分析

在你的代码中,函数change接收的是变量a的值,而不是它的引用或指针。这意味着在函数change中,参数value是变量a的一个副本,它们在内存中的地址是不同的。因此,当你在函数change中修改value的值时,这个改变不会影响到原始变量a

这就是为什么打印出来的地址是不同的,并且变量a的值没有发生改变。如果你想在函数中修改变量a的值,你需要使用引用或指针。例如:

void change(int& value)
{cout<<"change:: "<<&value<<endl;value = 20;
}

或者

void change(int* value)
{cout<<"change:: "<<value<<endl;*value = 20;
}

在C++中,当我们将一个变量作为参数传递给一个函数时,通常有两种方式:值传递和引用传递。

  1. 值传递:当我们通过值传递一个变量时,实际上是在内存中创建了这个变量的一个副本。这个副本有自己的内存地址,与原始变量的地址不同。因此,当我们在函数中修改这个副本的值时,原始变量的值并不会改变。
  2. 引用传递:当我们通过引用或指针传递一个变量时,我们实际上是将原始变量的内存地址传递给了函数。因此,函数中对参数的任何修改都会直接反映到原始变量上,因为它们共享同一块内存空间

二、一级指针 传地址

#include <iostream>
using namespace std;
#include <cstring>
using namespace std;
void change(int* value)
{cout<<"change:: "<<value<<endl;*value = 20;
}int main()
{int a = 10;cout<<"main:: "<<&a<<endl;change(&a);cout << a << endl;return 0;
}

在这里插入图片描述

可见value 和 a的地址是相同的

三、一级指针 判断改变p的值不会影响到函数外部的指针


#include <stdio.h>
#include <stdlib.h>
#include<iostream>
using namespace std;
void change(int *pp) { // int* pp=&Q;cout<<"pp的值"<<pp<<endl;cout<<"pp的地址"<<&pp<<endl;pp = (int *)malloc(sizeof(int));cout<<"malloc之后 pp的值"<<pp<<endl;cout<<"malloc之后 pp的地址"<<&pp<<endl;}int main() {int Q=3;int *p = &Q;cout<<"Q的地址"<<&Q<<endl;cout<<"p的值"<<p<<endl;cout<<"p的地址"<<&p<<endl;change(p);cout<<"malloc之后  p的值"<<p<<endl;cout<<"malloc之后  p的地址"<<&p<<endl;cout<<"malloc之后 Q的值 "<<*p<<endl;free(p);return 0;
}

产生中断,也就是说上面的程序存在问题,有什么问题呢?

为什么一级指针改变p的值不会影响到外部的指针

这是因为在C语言中,函数参数是通过值传递的。当你将一个一级指针作为参数传递给一个函数时,实际上传递的是这个指针的值(是它保存的内存地址,也就是它所指向的内存地址,)的一个副本。也就是说,str 的值等于 p的值,但str 本身的地址 和 p 本身的地址是不同的。

比如:

在这里插入图片描述

因为 str 和 p 的地址是不同的,所以他们是不同的指针。

在函数内部,你可以修改这个副本(把新分配的内存地址空间赋值给p,p的值(也就是p指向的内存空间变了)),但是这个修改不会影响到原来的指针。

举个例子,假设你有一个指针p,它的值(也就是它所指向的内存地址)是0x1000。当你将p作为参数传递给一个函数时,这个函数会收到一个值为0x1000的新指针。如果你在函数内部改变这个新指针的值(例如将它设置为NULL),那么这个改变只会影响到这个新指针,而不会影响到原来的p

因此,当你在函数内部改变一级指针的值时,这个改变不会影响到外部的指针。

执行malloc 函数 之后

在这里插入图片描述
在这里插入图片描述

为什么指向同一个内存地址的两个一级指针,在函数内部改变这个新的一级指针的值时,这个改变不会影响到原来的p。

这是因为在C语言中,函数参数是通过值传递的。这意味着当你将一个指针(无论是一级指针还是二级指针)作为参数传递给一个函数时,实际上传递的是这个指针的值(也就是它所指向的内存地址)的一个副本。

当你传递一个一级指针p给一个函数时,函数会收到一个新的一级指针,这个新的一级指针和p有相同的值(也就是它们都指向同一个内存地址)。但是,这个新的一级指针和p并不是同一个指针(他们本身的地址不同),它们只是有相同的值而已。因此,当你在函数内部改变这个新的一级指针的值时,这个改变不会影响到原来的p

换句话说,当你在函数内部改变一级指针的值时,你实际上是改变了这个一级指针副本的值,而不是原来的一级指针p。因为副本和原来的一级指针p是两个不同的变量,它们只是恰好有相同的值(也就是它们都指向同一个内存地址)。所以,改变副本的值并不会影响到原来的一级指针p

四、二级指针 判断改变p的值不会影响到函数外部的指针

#include <stdio.h>
#include <stdlib.h>void change(int **pp) {*pp = (int *)malloc(sizeof(int));**pp = 100;
}int main() {int *p = NULL;change(&p);printf("%d\n", *p);  // 输出:100free(p);return 0;
}

在你的代码中,change函数的参数是一个二级指针pp。当你调用change(&p)时,你实际上是将指针p的地址传递给了函数。这个地址就是二级指针pp所指向的地址。

因此,pp&p实际上是同一个地址。当你在函数中通过解引用操作符*来访问并修改pp时,你实际上是在修改原始指针p的值。这就是为什么这段代码能够成功运行并正确地修改p的值。

在这里插入图片描述
在这里插入图片描述

为什么指向同一个一级指针的二级指针,当我在函数内部改变这个新的二级指针所指向的一级指针时,这个改变会影响到原来的一级指针。

这是因为当你传递一个二级指针到一个函数时,你实际上是传递了一个指向一级指针的指针的副本。这个副本指向的是原始一级指针的内存地址。因此,当你在函数内部改变这个二级指针所指向的一级指针时,你实际上是改变了原始一级指针的值。

举个例子,假设你有一个一级指针p,它的值(也就是它所指向的内存地址)是0x1000。然后你有一个二级指针pp,它的值(也就是它所指向的内存地址)是p的地址。当你将pp作为参数传递给一个函数时,这个函数会收到一个新的二级指针,这个新的二级指针和pp有相同的值(也就是它们都指向同一个一级指针)。但是,这个新的二级指针和pp并不是同一个二级指针,它们只是有相同的值而已。然而,由于这个新的二级指针和pp都指向同一个一级指针,所以当你在函数内部改变这个新的二级指针所指向的一级指针(例如将它设置为NULL),那么这个改变会影响到原来的一级指针。

因此,当你在函数内部改变二级指针所指向的一级指针时,这个改变会影响到外部的一级指针。

这个为什么拷贝成功了呢?

在讲述之前,先注意p是二级指针,*p是一级指针,GetMemory(&str)传参时,指针变量p申请内存空间,这时有两个内存空间,str的地址拷贝给p的内存空间,即p的内存空间的内容是str的地址,这时p指向str的内存空间—>p = &str,*p = str,p 和 str两个名字,其实是相等的,指向的是同一块内存空间(上述程序在此时是NULL)。 p = (char )malloc(sizeof(100)),在堆上面申请一个100的内存空间,返回这个内存空间的首地址赋值给p;*p是一级指针,p(str)内存空间就有值了,不再是NULL,内存空间的内容是这个地址,也就是p和str指向这块内存空间,这个内存空间可以存放100个变量的地址,所以指针是存放其他变量地址的变量还是对的。接下来的strcpy(str, “hello world”)也就成功了,free(str)释放掉str(*p)所指向的内存空间.请注意,在这个过程中,p的内存空间的地址始终是str的地址,没有改变。

以上内容纯粹个人理解,如有不对之处,欢迎批评指正。


在这里插入图片描述
在这里插入图片描述

总结 二级指针和一级指针作为函数参数的区别

二级指针和一级指针作为函数参数传递时的区别

一级指针和二级指针在作为函数参数传递时的主要区别在于它们可以修改的内容。

当你传递一个一级指针到一个函数时,你可以通过这个指针修改它所指向的值,但是你不能改变这个指针本身所指向的内存地址。例如:

void func(int *p) {*p = 5;  // 可以修改p所指向的值p = NULL;  // 这里改变p的值不会影响到函数外部的指针
}

而当你传递一个二级指针到一个函数时,你既可以修改这个指针所指向的值,也可以改变这个指针本身所指向的内存地址。例如:

void func(int **p) {**p = 5;  // 可以修改p所指向的值*p = NULL;  // 这里改变*p的值会影响到函数外部的指针
}

因此,如果你想在一个函数中动态地改变一个指针所指向的内存地址,你需要传递一个二级指针。如果你只是想通过一个函数来改变一个值,那么传递一级指针就足够了。

在C++中,一级指针和二级指针作为函数参数有着不同的用途。

  1. 一级指针:当我们将一个一级指针作为函数参数时,我们可以在函数内部修改该指针所指向的值,但不能改变该指针本身。例如:
void changeValue(int *p) {*p = 100;  // 修改p所指向的值
}int main() {int a = 10;int *p = &a;changeValue(p);cout << a << endl;  // 输出:100return 0;
}

在上述代码中,我们将一级指针p作为参数传递给了changeValue函数,并在函数内部修改了p所指向的值。因此,当我们回到主函数并打印出a的值时,输出的结果是100。

  1. 二级指针:当我们将一个二级指针作为函数参数时,我们可以在函数内部修改该二级指针所指向的一级指针本身。例如:
void changePointer(int **pp) {*pp = (int *)malloc(sizeof(int));**pp = 100;  // 修改pp所指向的一级指针所指向的值
}int main() {int *p = NULL;changePointer(&p);cout << *p << endl;  // 输出:100free(p);return 0;
}

在上述代码中,我们将一级指针p的地址(也就是一个二级指针)作为参数传递给了changePointer函数,并在函数内部修改了该二级指针所指向的一级指针本身。因此,当我们回到主函数并打印出*p的值时,输出的结果是100。


#include <stdio.h>
#include <stdlib.h>
#include<iostream>
using namespace std;
void change(int **npp) {cout<<"npp的值"<<npp<<endl;cout<<"npp的地址"<<&npp<<endl;*npp = (int *)malloc(sizeof(int));**npp = 100;cout<<"malloc之后 npp的值"<<npp<<endl;cout<<"malloc之后 npp的地址"<<&npp<<endl;
}int main() {int Q=3;int *p = &Q;int **pp=&p;cout<<"Q的地址"<<&Q<<endl;cout<<"p的值"<<p<<endl;cout<<"p的地址"<<&p<<endl;cout<<"pp的值"<<pp<<endl;cout<<"pp的地址"<<&pp<<endl;change(&p);cout<<"malloc之后  p的值"<<p<<endl;cout<<"malloc之后  p的地址"<<&p<<endl;cout<<"malloc之后  pp的值"<<pp<<endl;cout<<"malloc之后  pp的地址"<<&pp<<endl;cout<<"malloc之后 Q的值 "<<*p<<endl;free(p);return 0;
}为什么这段代码中 pp 和npp 操作的是同一块内存空间

当然可以。让我们用一个比喻来理解一下:

假设你有
一个房子(变量),这个房子有一个地址(指针)。你把这个地址写在一张纸上(一级指针作为函数参数),然后给了你的朋友(函数)。你的朋友可以通过这个地址找到你的房子,并且可以改变房子里面的东西(修改指针所指向的值),但是他不能改变房子的位置(不能改变指针本身)。

现在,假设你不仅把房子的地址写在纸上,而且还把这张纸的地址(二级指针)也写在另一张纸上,然后给了你的朋友。这次,你的朋友不仅可以改变房子里面的东西,还可以改变第一张纸上写的地址(可以改变一级指针本身)。这就好比他可以把第一张纸上的地址擦掉,然后写上一个新的地址。这样,当你看第一张纸时,你会发现房子的"位置"已经改变了。

希望这个比喻能帮助你理解一级指针和二级指针作为函数参数的区别!

例子


int recvMsg(int cfd, char** msg)
{if(msg == NULL || cfd <= 0){
return -1;}// 接收数据// 1. 读数据头int len = 0;readn(cfd, (char*)&len, 4);len = ntohl(len);printf("数据块大小: %d\n", len);// 根据读出的长度分配内存char *buf = (char*)malloc(len+1);int ret = readn(cfd, buf, len);if(ret != len){return -1;}buf[len] = '\0';*msg = buf;return ret;
}

在这个函数中,二级指针msg被用作传出参数,这是因为函数需要修改外部变量的值。在函数内部,msg指向的内存被分配给了新的数据,然后这个新的地址被赋值给了*msg。如果我们只使用一级指针作为参数,那么在函数内部对指针的修改(例如分配新的内存)将不会影响到外部的指针,因为函数参数是通过值传递的。

使用二级指针作为参数允许我们在函数内部修改一级指针的值(即改变一级指针所指向的地址),这样我们就可以将新分配的内存地址传递回函数的调用者。这就是为什么在这个函数中选择二级指针作为传出参数,而不是一级指针。

如果你选择一级指针作为传出参数,那么在函数内部对指针的修改(例如分配新的内存)将不会影响到外部的指针,因为函数参数是通过值传递的。这就意味着,即使你在函数内部为指针分配了新的内存,这个新的内存地址也无法传递回函数的调用者。

下面是一个示例,展示了如果使用一级指针作为传出参数会发生什么:

void recvMsg(int cfd, char* msg)
{// 假设len是通过某种方式获取的int len = 100;// 分配内存char *buf = (char*)malloc(len+1);// 假设我们已经将数据读入buf// ...buf[len] = '\0';msg = buf;  // 这里只是修改了函数内部的msg副本,对外部的msg没有影响
}int main() {char *str = NULL;recvMsg(cfd, str);// 此时str仍然是NULL,因为recvMsg函数内部对msg的修改并未影响到strreturn 0;
}

在上述代码中,recvMsg函数接收一个一级指针msg作为参数。然而,由于msg是通过值传递的,所以在函数内部对msg的修改并不会影响到外部的str。这就是为什么在调用recvMsg(cfd, str)后,str仍然是NULL
= 100;
// 分配内存
char buf = (char)malloc(len+1);
// 假设我们已经将数据读入buf
// …
buf[len] = ‘\0’;
msg = buf; // 这里只是修改了函数内部的msg副本,对外部的msg没有影响
}

int main() {
char *str = NULL;
recvMsg(cfd, str);
// 此时str仍然是NULL,因为recvMsg函数内部对msg的修改并未影响到str
return 0;
}

在上述代码中,`recvMsg`函数接收一个一级指针`msg`作为参数。然而,由于`msg`是通过值传递的,所以在函数内部对`msg`的修改并不会影响到外部的`str`。这就是为什么在调用`recvMsg(cfd, str)`后,`str`仍然是`NULL`。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot Web MVC

文章目录 一、Spring Boot Web MVC 概念二、状态码三、其他注解四、响应操作 一、Spring Boot Web MVC 概念 Spring Web MVC 是⼀个 Web 框架&#xff0c;一开始就包含在Spring 框架里。 1. MVC 定义 软件⼯程中的⼀种软件架构设计模式&#xff0c;它把软件系统分为模型、视…

uniapp实现路线规划

UniApp是一个基于Vue.js框架开发的跨平台应用开发框架&#xff0c;可以同时构建iOS、Android、H5等多个平台的应用。它使用了基于前端技术栈的Web开发方式&#xff0c;通过编写一套代码&#xff0c;即可在不同平台上运行和发布应用。 UniApp具有以下特点&#xff1a; 跨平台开…

【设计模式】第8节:结构型模式之“适配器模式”

一、简介 适配器模式是用来做适配的&#xff0c;它将不兼容的接口转换为可兼容的接口&#xff0c;让原本由于接口不兼容而不能一起工作的类可以一起工作。 适配器模式角色&#xff1a; 请求者client&#xff1a;调用服务的角色目标Target&#xff1a;定义了Client要使用的功…

Window下coturn服务器的搭建

Window下搭建coturn服务器&#xff1a; 准备材料&#xff1a; 1、安装Cygwin&#xff0c;地址&#xff1a;https://cygwin.com/install.html 由于Window无法直接部署coturn&#xff0c;因此需要下载安装Cygwin在Window上部署Linux虚拟环境。 在安装的时候需要安装几下packe…

Azure机器学习 - 使用与Azure集成的Visual Studio Code实战教程

本文介绍如何启动远程连接到 Azure 机器学习计算实例的 Visual Studio Code。 借助 Azure 机器学习资源的强大功能&#xff0c;使用 VS Code 作为集成开发环境 (IDE)。 在VS Code中将计算实例设置为远程 Jupyter Notebook 服务器。 关注TechLead&#xff0c;分享AI全维度知识。…

目标检测 图像处理 计算机视觉 工业视觉

目标检测 图像处理 计算机视觉 工业视觉 工业表盘自动识别&#xff08;指针型和数值型&#xff09;智能水尺识别电梯中电动车识别&#xff0c;人数统计缺陷检测&#xff08;半导体&#xff0c;电子元器件等&#xff09;没带头盔检测基于dlib的人脸识别抽烟检测和睡岗检测/驾驶疲…

Java选择与循环

1.选择 前言&#xff1a;什么是选择呢&#xff1f;在我们的人生中处处面临着选择&#xff0c;比如说在学校你可以选择玩&#xff0c;摆烂&#xff0c;当然也可以选择努力写代码&#xff0c;刷题。什么样的选择就会面临什么样的结果。 其实程序和人生一样&#xff1a;顺序中夹杂…

大数据技术之集群数据迁移

文章目录 数据治理之集群迁移数据 数据治理之集群迁移数据 准备两套集群&#xff0c;我这使用apache集群和CDH集群。 启动集群 启动完毕后&#xff0c;将apache集群中&#xff0c;hive库里dwd,dws,ads三个库的数据迁移到CDH集群 在apache集群里hosts加上CDH Namenode对应域名并…

IPv4首部格式

IPv4首部格式 IPv4数据报的首部格式及其内容是实现IPv4协议各种功能的基础。 在TCPIP标准中&#xff0c;各种数据格式常常以32比特(即4字节)为单位来描述。 IPv4首部格式图 ## IPv4数据报的组成 主要由固定部分(20字节)可变部分(最大40字节) - 固定部分是指每个IPv4数据报都必…

Java使用pdfbox进行pdf和图片之间的转换

简介 pdfbox是Apache开源的一个项目,支持pdf文档操作功能。 官网地址: Apache PDFBox | A Java PDF Library 支持的功能如下图.引入依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox-app</artifactId><version>…

去除短视频平台水印 | 一键下载神器

当咱们这些视频创作者在短视频平台找素材的时候&#xff0c;经常会碰到下载下来居然带着平台水印的烦恼&#xff0c;这可让咱们的创作受到了限制和困扰不过别着急&#xff01;咱这就推荐几款超方便的短视频一键去水印下载工具&#xff0c;帮你快速去掉水印&#xff0c;轻松搞定…

你没有见过的 git log 风格

背景 git大家都不陌生&#xff0c;git log 也是大家经常用的指令&#xff0c;今天分享三种 git log的美化格式&#xff0c;大家看看哪种更易读。 git log -15 --graph --decorate --oneline 带有 pretty 格式的git log 风格 log --color --graph --prettyformat:‘%Cred%h%C…

生态扩展:Flink Doris Connector

生态扩展&#xff1a;Flink Doris Connector 官网地址&#xff1a; https://doris.apache.org/zh-CN/docs/dev/ecosystem/flink-doris-connector flink的安装&#xff1a; tar -zxvf flink-1.16.0-bin-scala_2.12.tgz mv flink-1.16.0-bin-scala_2.12.tgz /opt/flinkflink环境…

华为防火墙 配置 SSLVPN

需求&#xff1a; 公司域环境&#xff0c;大陆客户端居家办公室需要连到公司域&#xff0c;这里可以在上海防火墙上面开通SSLVPN&#xff0c;员工就可以透过SSLVPN连通上海公司的内网&#xff0c;但是由于公司域控有2个站点&#xff0c;一个在上海&#xff0c;一个在台北&…

【2023年MathorCup高校数学建模挑战赛-大数据竞赛】赛道A:基于计算机视觉的坑洼道路检测和识别 python 代码解析

【2023年MathorCup高校数学建模挑战赛-大数据竞赛】赛道A&#xff1a;基于计算机视觉的坑洼道路检测和识别 python 代码解析 1 题目 坑洼道路检测和识别是一种计算机视觉任务&#xff0c;旨在通过数字图像&#xff08;通常是地表坑洼图像&#xff09;识别出存在坑洼的道路。这…

框架安全-CVE 复现Apache ShiroApache Solr漏洞复现

文章目录 服务攻防-框架安全&CVE 复现&Apache Shiro&Apache Solr漏洞复现中间件列表常见开发框架Apache Shiro-组件框架安全暴露的安全问题漏洞复现Apache Shiro认证绕过漏洞&#xff08;CVE-2020-1957&#xff09;CVE-2020-11989验证绕过漏洞CVE_2016_4437 Shiro-…

分享者 - 携程旅游创作者搬砖项目图文教程

大家好&#xff01;携程这个出行旅游平台相信大家都不陌生吧。 每天都有大量的旅客在里面浏览攻略&#xff0c;寻找灵感和旅游建议。 那么&#xff0c;我们的项目就是把一些优质的小红书平台上的旅游攻略或作品&#xff0c;经过处理后搬运到携程平台上发布。 这个项目如何操作呢…

06_es分布式搜索引擎2

一、DSL查询文档 1.DSL查询分类 ①查询所有&#xff1a;match_all ②全文检索&#xff1a;利用分词器对用户输入的内容分词&#xff0c;倒排索引去匹配 match_query multi_match_query ③精确查询&#xff1a;根据精确词条查找数据&#xff0c;查找的是keyword,数值,日期,b…

掌握口才与演讲技巧,让你职场中脱颖而出

在职场竞争日趋激烈的今天&#xff0c;口才和演讲能力已经成为一个人成功的重要标志之一。掌握了优秀的口才与演讲技巧&#xff0c;不仅可以帮助你在工作中更好地表达自己和传达信息&#xff0c;同时也可以让你在同事和上级心中留下深刻印象&#xff0c;从而在职场中脱颖而出&a…

JVM修炼印记之初识

文章目录 JVM认识JVM的功能常见JVMHotSpot的发展历程 JVM认识 Java虚拟机&#xff08;Java Virtual Machine&#xff0c;JVM&#xff09;是一个用于执行Java字节码的虚拟计算机。它是Java语言的核心&#xff0c;可以在不同的操作系统和硬件平台上运行Java程序。 JVM负责将Java…