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

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

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

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.mzph.cn/news/127306.shtml

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

相关文章

任正非说:人家问我:“你怎么一天到晚游手好闲?”我说我是管长江的堤坝的。

你好&#xff01;这是华研荟【任正非说】系列的第26篇文章&#xff0c;让我们聆听任正非先生的真知灼见&#xff0c;学习华为的管理思想和管理理念。 一、我们不是靠人来领导这个公司&#xff0c;我们用规则的确定性来对付结果的不确定。人家问我&#xff1a;“你怎么一天到晚游…

HarmonyOS UI 开发

引言 HarmonyOS 提供了强大的 UI 开发工具和组件&#xff0c;使开发者能够创建吸引人的用户界面。本章将详细介绍在 HarmonyOS 中应用 JS、CSS、HTML&#xff0c;HarmonyOS 的 UI 组件以及如何自定义 UI 组件。 目录 JS、CSS、HTML 在 HarmonyOS 中的应用HarmonyOS 的 UI 组…

基于单片机设计的自动门控制系统

一、项目介绍 随着科技的不断发展&#xff0c;自动门成为公共场所、商业建筑和住宅社区等地的常见设施。自动门的出现使得进出门的操作更加便捷&#xff0c;提高了人们的生活质量和工作效率。为了实现自动门的开关控制&#xff0c;本项目基于单片机设计了一套自动门控制系统。…

数据结构───链表

花费一个周时间学完了链表&#xff08;的一部分&#xff09;&#xff0c;简单总结一下。 链表的学习离不开画图&#xff0c;将其抽象成一种逻辑模型&#xff0c;可以减少思考时间&#xff0c;方便理解。 链表大致分为8种结构&#xff0c;自己学习并实现了两种结构&#xff0c;也…

ubuntu无网络连接,没有网络标识,快速解决方法

在这里插入代码片当我们装虚拟机的时候&#xff0c;需要用到网络时发现没有网络连接&#xff0c;且右上角没有网络标识符&#xff0c;这时只需要简单的输入一下三个命令即可 sudo nmcli networking offsudo nmcli networking onsudo service network-manager restart然后重启客…

问题 D: 免费馅饼(类数塔问题)

免费馅饼 都说天上不会掉馅饼&#xff0c;但有一天gameboy正走在回家的小径上&#xff0c;忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了&#xff0c;这馅饼别处都不掉&#xff0c;就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了&#xff0c;所以…

【DriveGPT学习笔记】自动驾驶汽车Autonomous Vehicle Planning

原文地址&#xff1a;DriveGPT - Lei Maos Log Book 自动驾驶汽车的核心软件组件是感知、规划和控制。规划是指在给定场景或一系列场景的情况下为自动驾驶汽车制定行动计划的过程&#xff0c;以实现安全和理想的自动驾驶。 用于规划的场景是从感知软件组件获得的。计划的行动将…

centos 7 kafka2.6单机安装及动态认证SASL SCRAM配置

目录 1.kfaka安装篇 1.1 安装jdk 1.2安装kafka 2.安全篇 2.1 kafka安全涉及3部份&#xff1a; 2.2 Kafka权限控制认证方式 2.3 SASL/SCRAM-SHA-256 配置实例 2.3.1 创建用户 2.3.2 创建 JAAS 文件及配置 3.测试 3.1 创建测试用户 3.2 配置JAAS 文件 3.2.1 生产者配…

arcgis删除细长图斑的方法

1、有一张图斑数据如下&#xff1a; 如上图&#xff0c;有很多细长的面要素&#xff0c;需要保留的仅是图中的块状要素。 2、首先要将被合并的要素进行拆分&#xff0c;具体拆分步骤如下&#xff1a; 将所有要素选中&#xff0c;点击高级编辑中的拆分按钮。 3、拆分后图斑就…

信号、进程、线程、I/O介绍

文章目录 信号进程进程通信线程可/不可重入函数线程同步互斥锁条件变量自旋锁读写锁 I/O操作阻塞/非阻塞I/OI/O多路复用存储映射I/O 信号 信号是事件发生时对进程的通知机制&#xff0c;可以看做软件中断。信号与硬件中断的相似之处在于其能够打断程序当前执行的正常流程。大多…

C语言每日一题(23)兔子的序列

牛客网 BC159 兔子的序列 题目描述 描述 兔子发现了一个数字序列&#xff0c;于是开始研究这个序列。兔子觉得一个序列应该需要有一个命名&#xff0c;命名应该要与这个序列有关。由于兔子十分讨厌完全平方数&#xff0c;所以兔子开创了一个新的命名方式&#xff1a;这个序列…

深度学习数据集大合集—疾病、植物、汽车等

最近又收集了一大批深度学习数据集&#xff0c;今天分享给大家&#xff01;废话不多说&#xff0c;直接上数据&#xff01; 1、招聘欺诈数据集 招聘欺诈数据集&#xff1a;共收集了 200,000 条数据&#xff0c;来自三个网站。 该数据集共收集了 200.000 条数据&#xff0c;分别…

短视频账号矩阵系统saas源码搭建/技术

一、短视频矩阵系统建模----技术api接口--获取用户授权 技术文档分享&#xff1a; 本系统采用MySQL数据库进行存储&#xff0c;数据库设计如下&#xff1a; 1.用户表&#xff08;user&#xff09;&#xff1a; - 用户ID&#xff08;user_id&#xff09; - 用户名&#xff08…

天体学爱好者基础知识-太阳系//未完待续,业余者的学习

难过的时候&#xff0c;仰望天空吧&#xff0c;人类有时候&#xff0c;做的事情真的太愚昧且无聊了&#xff0c;渺小的尘埃&#xff0c;也可以飘际宇宙。 太阳系-八大行星 卫星围绕着恒星公转。行星必须围绕着恒星公转。 什么是行星&#xff1f;行星和恒星、卫星有什么区别&am…

Fourier分析导论——第2章——Fourier级数的基本属性(E.M. Stein R. Shakarchi)

第 2 章 Fourier级数的基本属性(Basic Properties of Fourier Series) Nearly fifty years had passed without any progress on the question of analytic representation of an arbitrary function, when an assertion of Fourier threw new light on the subject. Thus…

yolov7模型轻量化改进之MobileOne骨干替换

本文在之前文章yolov7分割训练的基础上进行改进: https://blog.csdn.net/qq_41920323/article/details/129464115?spm=1001.2014.3001.5502 具体GitHub工程下载,环境安装配置,数据准备等,请借鉴之前的文章,此处只介绍如何进行改进。 MobileOne地址:https://github.com…

vue(32) : win10创建vue2基础前端框架

vue2element-uiaxios 1.创建vue2项目 开发工具为HBuilderX 3.7.3 1.1.新建项目 1.2.普通项目-vue项目(2.6.10) 等待创建项目 2.安装element-ui组件 2.1右键左下角开始图标 2.2.cd进入项目目录,执行安装element-ui npm i element-ui -S 2.3.main.js引入配置 import {Paginat…

【HarmonyOS】服务卡片 API6 JSUI跳转不同页面并携带参数

【关键字】 服务卡片、卡片跳转不同页面、卡片跳转页面携带参数 【写在前面】 本篇文章主要介绍开发服务卡片时&#xff0c;如何实现卡片点击跳转不同页面&#xff0c;并携带动态参数到js页面。在此篇文章“服务卡片 API6 JSUI跳转不同页面”中说明了如果跳转不同页面&#xf…

VMware Workstation 17安装教程之创建普通用户

创建普通用户 Subscription Manager。它指的是红帽产品订阅服务&#xff0c;是红帽公司的一项收费服务&#xff0c;我们暂时不需要。 安装完毕后等待重启 安装后的收尾工作 接受红帽许可协议 返回到初始化界面&#xff0c;单击FINISH CONFIGURATION按钮进行确认后&#xff0…

4G物联模组产品

4G物联模组产品 文章目录 4G物联模组产品1.功能2.优势3.规格参数3.1.额定最大值3.2.尺寸规格 4.内部实物图5.产品功能说明5.1.通信功能5.2.GPS定位5.3.充放电管理5.4.告警和保护5.5.软件升级5.6.软件调测 6.通信协议6.1流程6.2.消息定义6.2.1.应用下发到云6.2.2.云下发到设备6.…