当函数参数为一级指针,二级指针
在讲述内容之前,先讲四点重要知识
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++中,当我们将一个变量作为参数传递给一个函数时,通常有两种方式:值传递和引用传递。
- 值传递:当我们通过值传递一个变量时,实际上是在内存中创建了这个变量的一个副本。这个副本有自己的内存地址,与原始变量的地址不同。因此,当我们在函数中修改这个副本的值时,原始变量的值并不会改变。
- 引用传递:当我们通过引用或指针传递一个变量时,我们实际上是将原始变量的内存地址传递给了函数。因此,函数中对参数的任何修改都会直接反映到原始变量上,因为它们共享同一块内存空间
二、一级指针 传地址
#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++中,一级指针和二级指针作为函数参数有着不同的用途。
- 一级指针:当我们将一个一级指针作为函数参数时,我们可以在函数内部修改该指针所指向的值,但不能改变该指针本身。例如:
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。
- 二级指针:当我们将一个二级指针作为函数参数时,我们可以在函数内部修改该二级指针所指向的一级指针本身。例如:
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`。