C 函数参数传递一级指针和二级指针的区别
文章目录
一、概念
1、一级指针和二级指针
指针保存的只是地址,这一点不用忘了
一级指针:指向变量的指针,保存的是该变量的地址;
二级指针:指向一级指针的指针,保存的也是地址,这个地址是一级指针变量的地址;
如:
int a = 1;
int* p = &a; //一级指针,p保存的是变量a的地址,*p 是取到变量a 的值
int** _p = &p; //二级指针,_p保存的一级指针变量p的地址,*_p 取到的是一级指针变量p的地址,**_p 取到一级指针p所指向地址的值(这里**_p 的值是1)
printf("%d\n", *p);
printf("%p\n", *_p);
printf("%d\n", **_p);
内存块ox0001 保存的是变量a的值1;
内存块ox0002 保存的是变量a的值的地址,如这里的ox0001;
内存块ox0003 保存的是一级指针变量p的地址,如这里的ox0002;
所以,p 拿到的值为1,_p 拿到的值是ox00002, **_p 拿到的值是1
(内存地址系统每次分配的都不一样)
二、函数参数为一级指针例子
1、程序一:指针类型为基本数据
#include <stdio.h>
void fun(int* p) {
int b = 100;
p = &b;
}
int main(int argc, char* argv[]) {
int a = 10;
int* q=&a;
printf("%d\n", a);
printf("%d\n", *q);
fun(q);
printf("%d\n", a);
printf("%d\n", *q);
return 0;
}
打印结果:
打印结果什么都没改变,传递了指针p进函数fun,并且改变了指针p的指向。就算外面a的值不变,但是修改了p的指向,那外面打印*p 应该是100才对,那为什么不会对外面的原始指针产生变化呢?
其实在函数传递参数时,编译器总会为每个函数参数制作一个副本,即拷贝;
例如:
void fun(int *p),指针参数p的副本为_p,编译器使_p=p,_p和p指向相同的内存空间,如果在函数内修改了_p所指向的内容,就会导致p的内容也做相应的改变;
但如果在函数内_p申请了新的内存空间或者指向其他内存空间,则_p指向了新的内存空间,而p依旧指向原来的内存空间,因此函数返回后p还是原来的p;
这样的话,不但没有实现功能,反而每次都申请新的内存空间,而又得不到释放,因为没有将该内存空间的地址传递出来,容易造成内存泄露;
所以,函数fun的函数体经过编译器编译之后的内容如下:
void fun(int* p) {
int* _p = p;
int b = 100;
_p = &b;
}
传递进来的一级指针在函数体内部的操作,全部都是对副本指针_p的操作;
如这里的函数体只是修改副本指针的指向,而原指针p还是指向变量a的地址,所以不会让外层的变量和指针产生变化;修改函数fun:
void fun(int* p) {
*p = 100;
}
打印结果:
这里就是修改了副本指针的指向内容,而副本指针指向的内容为变量a的值,所以就会对外面的变量产生变化。
2、程序二:参数为结构体(不是指针类型)
typedef struct User {
char* name;
int age;
};
void set_user_info(User user) {
user.name = "lisi";
user.age = 30;
}
int main(int argc, char* argv[]) {
User user;
user.name = "zhangsan";
user.age = 26;
printf("name=%s,age=%d\n", user.name, user.age);
set_user_info(user);
printf("name=%s,age=%d\n", user.name, user.age);
return 0;
}
打印结果:
原因和程序一 一样,函数参数发生了拷贝,经过编译后的函数set_user_info的内容如下:
void set_user_info(User user) {//这里的user 是原数据
User _user = user;//拷贝
_user.name = "lisi";
_user.age = 30;
}
函数体内,变量_user 和原始的变量user 是两个不同的变量,也是两块不同的地址;程序打印如下:
void set_user_info(User _user) {
_user.name = "lisi";
_user.age = 30;
printf("user=%p\n", &_user);
}
int main(int argc, char* argv[]) {
User user;
user.name = "zhangsan";
user.age = 26;
printf("user=%p\n", &user);
set_user_info(user);
return 0;
}
所以在函数体内的操作只是对副本的变量的操作,不会对原始变量的内容产生改变
3、程序三:参数类型为结构体指针
功能:打印函数的参数指针和原指针是否指向同一块地址
typedef struct User {
char* name;
int age;
};
void set_user_info(User* user) {
user->name = "lisi";
user->age = 30;
printf("user=%p\n", user);
}
int main(int argc, char* argv[]) {
User* user;
user = (User*)malloc(sizeof(User));
user->name = "zhangsan";
user->age = 26;
printf("user=%p\n", user);
set_user_info(user);
free(user);//主动申请的内存也需要手动释放
return 0;
}
打印结果:
说明:函数参数为指针的,发生拷贝之后,副本指针很原始指针都是指向同一块地址,所以修改副本指针指向的内容时,外面的变量也会发生变化(和程序一原理一样,这里重复是针对参数的指针类型为结构体类型下),示例代码如下:
typedef struct User {
char* name;
int age;
};
void set_user_info(User* user) {
user->name = "lisi";
user->age = 30;
}
int main(int argc, char* argv[]) {
User* user;
user = (User*)malloc(sizeof(User));
user->name = "zhangsan";
user->age = 26;
printf("name=%s,age=%d\n", user->name, user->age);
set_user_info(user);
printf("name=%s,age=%d\n", user->name, user->age);
free(user);//主动申请的内存也需要手动释放
return 0;
}
打印结果:
so:在传递一级指针时,只有对指针所指向的内存变量做操作才是有效的;
函数中的所有操作都是对拷贝的副本指针进行操作,如果在函数中申请内存赋值给传进来的指针变量,实际上这块申请的内存是给副本指针赋值的,不是对外面的指针赋值,所以不会对外面的指针产生影响
在程序三中,需要对手动给指针变量user 申请内存,才不至于后续的操作会报:- The variable ‘user’ is being used without being initialized. 如果我们想只是申请一个指针变量,然后传递给函数,在函数中给指针赋值,需要怎么做呢? 这时候就需要使用二级指针了;
三、函数参数为二级指针例子
1、程序四:二级指针类型为基本数据类型
void fun2(int** _p) {
int b = 100;//相当于开辟一块内存
*_p = &b;
}
int main(int argc, char* argv[]) {
int a = 10;
int* p = &a;
printf("a=%d\n", *p);// node1
fun2(&p);
printf("*p=%d\n", *p);// node2
printf("a=%d\n", a);// node3
return 0;
}
打印结果:
先看node1 处的打印 a=10,这个没什么问题;
在没执行函数fun2 中的 *_p = &b 前,各个变量的指向链如下图所示
在执行之后,如下图所示
一级指针 p 保存了ox0004 的地址,所以node2打印*p 打印的是100,变量a的内容从始至终都没有修改过,所以node3打印了10。
从上图可知,在声明一级指针后,没对其进行初始化,也可使用二级指针形式,修改一级指针的指向,从而得到值;
代码如下:
void fun2(int** p) {
int b = 100;//相当于开辟一块内存
*p = &b;
}
int main(int argc, char* argv[]) {
int* p;
fun2(&p);
printf("*p=%d\n", *p);
return 0;
}
打印结果
2、程序五:二级指针类型为结构体
void get_user_info(User** user) {
User* _user = (User*)malloc(sizeof(User));
_user->name = "lingtao";
_user->age = 26;
*user = _user;
}
int main(int argc, char* argv[]) {
User* user;
get_user_info(&user);
printf("user.name=%s\nuser.age=%d\n", user->name, user->age);
free(user);//不使用了,需要手动释放内存
user = 0;
return 0;
}
打印结果:
3、程序六:通过返回值形式给外面的指针赋值
User* get_user_info2() {
User* _user = (User*)malloc(sizeof(User));
_user->name = "lingtao";
_user->age = 26;
return _user;
}
int main(int argc, char* argv[]) {
User* user;
//get_user_info(&user);
user = get_user_info2();
printf("user.name=%s\nuser.age=%d\n", user->name, user->age);
free(user);//不使用了,需要手动释放内存
user = 0;
return 0;
}
程序六就不多说了,也是发生拷贝,和传参的参数拷贝类似
SO:在传递二级指针时,只有对指针的指向做改变才是有效的;
总结:
在传递一级指针时,只有对指针所指向的内存变量做操作才是有效的;
在传递二级指针时,只有对指针的指向做改变才是有效的;