欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C 函数参数传递一级指针和二级指针的区别

程序员文章站 2024-01-01 19:43:40
...

一、概念

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

C 函数参数传递一级指针和二级指针的区别

内存块ox0001 保存的是变量a的值1;
内存块ox0002 保存的是变量a的值的地址,如这里的ox0001;
内存块ox0003 保存的是一级指针变量p的地址,如这里的ox0002;
所以,p 拿到的值为1,_p 拿到的值是ox00002, **_p 拿到的值是1

C 函数参数传递一级指针和二级指针的区别
(内存地址系统每次分配的都不一样)

二、函数参数为一级指针例子

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

打印结果:
C 函数参数传递一级指针和二级指针的区别
打印结果什么都没改变,传递了指针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;
}

打印结果:
C 函数参数传递一级指针和二级指针的区别
这里就是修改了副本指针的指向内容,而副本指针指向的内容为变量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;
}

打印结果:C 函数参数传递一级指针和二级指针的区别
原因和程序一 一样,函数参数发生了拷贝,经过编译后的函数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;
}

C 函数参数传递一级指针和二级指针的区别
所以在函数体内的操作只是对副本的变量的操作,不会对原始变量的内容产生改变

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

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

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

打印结果:
C 函数参数传递一级指针和二级指针的区别

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

打印结果:
C 函数参数传递一级指针和二级指针的区别
先看node1 处的打印 a=10,这个没什么问题;
在没执行函数fun2 中的 *_p = &b 前,各个变量的指向链如下图所示
C 函数参数传递一级指针和二级指针的区别
在执行之后,如下图所示
C 函数参数传递一级指针和二级指针的区别

一级指针 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;
}

打印结果
C 函数参数传递一级指针和二级指针的区别

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

打印结果:
C 函数参数传递一级指针和二级指针的区别

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

总结:

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

相关标签: c语言 指针

上一篇:

下一篇: