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

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

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

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);  // 输出:100
    free(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;  // 输出:100
    return 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;  // 输出:100
    free(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的修改并未影响到str
    return 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`。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ECharts折线图去掉图例和线段上的小圆点

官方的初始效果 折线图的图例有小圆点&#xff0c;并且图表中也有小圆点 最终效果 去掉图例和图标中的小圆点 并且柱状图和折线图的图例要不同 代码实现 去掉图例小圆点 官方文档 itemStyle: { opacity: 0 } 折线图中的小圆点去掉 官方文档 两个代码二选一就行&#x…

设计模式04———桥接模式 c#

桥接模式&#xff1a;将一个事物从多个维度抽象出来&#xff0c;采用 分离 和 组合 的方式 替代 原本类的继承 桥接模式&#xff08;Bridge Pattern&#xff09;是一种软件设计模式&#xff0c;属于结构型模式&#xff0c;它用于将抽象部分与具体实现部分分离&#xff0c;以便它…

Jorani远程命令执行漏洞 CVE-2023-26469

Jorani远程命令执行漏洞 CVE-2023-26469 漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"Jorani"Hunter: web.title"Jorani" 漏洞复现1. 获取cookie2. 构造poc3. 执行命令 漏洞描述 Jorani是一款开源的员工考勤和休假管理系统&#xff0c;适用于中小型企业…

EASYX实现多物体运动

eg1:单个物体运动使用easyx实现单个小球的运动 #include <stdio.h> #include <easyx.h> #include <iostream> #include <math.h> #include <stdlib.h> #include <conio.h> #include <time.h> #define PI 3.14 #define NODE_WIDTH 4…

API接口的定义|电商API接口的接入测试和参数说明【附代码实例教程】

一 . API接口的定义 API全称Application Programming Interface&#xff0c;即应用程序编程接口&#xff0c;是一些预先定义的函数&#xff0c;或指软件系统不同组成部分衔接的约定&#xff0c;用于传输数据和指令&#xff0c;使应用程序之间可以集成和共享数据资源。 简单来…

Android拖放startDragAndDrop拖拽onDrawShadow静态添加xml布局View,Kotlin(4)

Android拖放startDragAndDrop拖拽onDrawShadow静态添加xml布局View&#xff0c;Kotlin&#xff08;4&#xff09; import android.content.ClipData import android.graphics.Canvas import android.graphics.Point import android.os.Bundle import android.util.Log import a…

Jetson NX FFmpeg硬件编解码实现

最近在用Jetson Xavier NX板子做视频处理&#xff0c;但是CPU进行视频编解码&#xff0c;效率比较地下。 于是便考虑用硬解码来对视频进行处理。 通过jtop查看&#xff0c;发现板子是支持 NVENC硬件编解码的。 1、下载源码 因为需要对ffmpeg进行打补丁修改&#xff0c;因此需…

无需服务器内网穿透Windows下快速搭建个人WEB项目

&#x1f4d1;前言 本文主要是windows下内网穿透文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ 参考自&#xff1a;Windows搭建web站点&#xff1a;免费内网穿透发布至公网 &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首…

Java算法:二分查找

一、 二分查找注意 前提是数组必须是有序的&#xff0c;否则无法正常工作。如果数组不是有序的&#xff0c;需要先对数组进行排序&#xff0c;然后才能使用二分查找算法。 二、二分查找高效算法 二分查找也称为折半查找&#xff0c;是一种在有序数组中查找目标元素的算法。它的…

【嵌入式开发学习02】esp32cam烧录human_face_detect实现人脸识别

Ubuntu20.04系统为esp32cam烧录human_face_detect 1. 下载esp-dl2. 安装esp-idf3. 烧录human_face_detect 如果使用ubuntu 16.04在后续的步骤中会报错如下&#xff0c;因为ubuntu 16.04不支持glibc2.23以上的版本&#xff08;可使用strings /lib/x86_64-linux-gnu/libc.so.6 | …

护眼灯有没有护眼的效果?适合学生儿童的五款护眼台灯推荐

如果不想家里的孩子年纪小小的就戴着眼镜&#xff0c;从小就容易近视&#xff0c;那么护眼灯的选择就非常重要了&#xff0c;但是市场上那么多品类&#xff0c;价格也参差不齐&#xff0c;到底怎么选呢&#xff1f;大家一定要看完本期内容。为大家推荐最热门的五款护眼台灯。 1…

HTML、CSS和JavaScript,实现换肤效果的原理

这篇涉及到HTML DOM的节点类型、节点层级关系、DOM对象的继承关系、操作DOM节点和HTML元素 还用到HTML5的本地存储技术。 换肤效果的原理&#xff1a;是在选择某种皮肤样式之后&#xff0c;通过JavaScript脚本来加载选中的样式&#xff0c;再通过localStorage存储。 先来回忆…

Spring MVC (Next-1)

1.Restful请求 restFul是符合rest架构风格的网络API接口,完全承认Http是用于标识资源。restFul URL是面向资源的&#xff0c;可以唯一标识和定位资源。 对于该URL标识的资源做何种操作是由Http方法决定的。 rest请求方法有4种&#xff0c;包括get,post,put,delete.分别对应获取…

CRM系统数据库是如何影响客户体验的?

CRM客户关系管理由概念到软件实体&#xff0c;已经有几十年的时间&#xff0c;随着信息技术的进步&#xff0c;数字化让CRM软件乘上快车&#xff0c;迅速成为各类企业的数字化管理工具。CRM客户管理系统的一个重要功能便是改善并提升客户体验&#xff0c;且CRM数据库是与客户体…

【笔记】excel怎么把汉字转换成拼音

1、准备好excel文件&#xff0c;复制需要转拼音列。 2、打开一个空白Word文档&#xff0c;并粘贴刚才复制的内容&#xff1b; 3、全选Word文档中刚粘贴的内容&#xff0c;点击「开始」选项卡「字体」命令组下的「拼音指南」&#xff0c;调出拼音指南对话框&#xff1b; 4、全…

如何调整职场心态,提高工作表现

文章目录 介绍职场分析对比历年职场需求开发者地域分布开发者工作状态职场晋升之路 职场经验控制情绪保持好奇心提升核心能力 职场转行结论 介绍 职场中的心态调整对于我们在工作中表现的影响非常重要。作为一名全栈开发者&#xff0c;我深知在 AI 算法和云技能领域工作的挑战…

【生物信息学】单细胞RNA测序数据分析:计算亲和力矩阵(基于距离、皮尔逊相关系数)及绘制热图(Heatmap)

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入必要的库1. 读取数据集2. 质量控制&#xff08;可选&#xff09;3. 基于距离的亲和力矩阵4. 绘制基因表达的Heatmap5. 基于皮尔逊相关系数的亲和力矩阵6. 代码整合 一、实验介绍 计算亲和力…

云服务器 centos 部署 code-server 并配置 c/c++ 环境

将你的云服务器改为 centos 8 为什么要将云服务器的操作系统改成 centos 8 呢&#xff1f;原因就是 centos 7 里面的配置满足不了 code-server 的需求。如果你使用的是 centos 7 那么就需要你升级一些东西&#xff0c;这个过程比较麻烦。我在 centos 7 上面运行 code-server 的…

[学习笔记]python绘制图中图(绘制站点分布图)

背景 在绘制站点分布图时&#xff0c;有时需要采用图中图的方式&#xff0c;以便于在一张图中尽可能多的表达信息。此处记录一下利用python matplotlib绘制图中图的脚本&#xff0c;方便然后查询。 包含数据 该绘图脚本中包含以下数据&#xff1a; CMONOC站点分布&#xff…

CCF_A 计算机视觉顶会CVPR2024投稿指南以及论文模板

目录 CVPR2024官网&#xff1a; CVPR2024投稿链接&#xff1a; CVPR2024 重要时间节点&#xff1a; CVPR2024投稿模板: WORD: LATEX : CVPR2024_AuthorGuidelines CVPR2024投稿Topics&#xff1a; CVPR2024官网&#xff1a; https://cvpr.thecvf.com/Conferences/2024CV…