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;
}

打印结果:
打印结果1
打印结果什么都没改变,传递了指针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;
}

打印结果:
打印结果2
这里就是修改了副本指针的指向内容,而副本指针指向的内容为变量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;
}

打印结果:打印结果3
原因和程序一 一样,函数参数发生了拷贝,经过编译后的函数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;
}

打印结果4
所以在函数体内的操作只是对副本的变量的操作,不会对原始变量的内容产生改变

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;
}

打印结果:
打印结果5
说明:函数参数为指针的,发生拷贝之后,副本指针很原始指针都是指向同一块地址,所以修改副本指针指向的内容时,外面的变量也会发生变化(和程序一原理一样,这里重复是针对参数的指针类型为结构体类型下),示例代码如下:

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;
}

打印结果:
打印结果6

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;
}

打印结果:
打印结果7
先看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;
}

打印结果
打印结果8

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;
}

打印结果:
打印结果9

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:在传递二级指针时,只有对指针的指向做改变才是有效的;

总结:

在传递一级指针时,只有对指针所指向的内存变量做操作才是有效的;
在传递二级指针时,只有对指针的指向做改变才是有效的;

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值