函数参数的值传递和地址传递

以下所有叙述如果有错误,烦请评论指正

变量、变量名、地址、指针、普通变量(以整型int为例)、指针变量
1、变量:变量可以看做是一个容器,这个容器用来存放数据,变量存放在内存中
2、变量名:变量名实际上是一个地址的符号,当程序编译的时候,操作系统会为变量在内存中分配内存空间,所以每一个变量都会有一个实际的内存地址。系统将变量名和内存地址关联起来,这样我们在操作变量名的时候,实际操作的其实是内存中某一地址对应的空间。
3、地址:程序是跑在内存中的,程序又将内存分成很多块,用来存储不同的信息。如何找到这些块,就是通过地址。相当于一个小区就是一片内存,而门牌号就是内存地址,通过门牌号就能找到家
4、指针:指针就是地址,变量在内存中的地址又被称作为指针而已
5、普通变量(int a):a是变量名,对应内存空间的大小是sizeof(int),对应地址假设是0x001,也就是地址0x001存放的是变量a的值,存放的数据类型是整型
6、指针变量(char *p):指针变量的本质还是一个变量,只不过存放的数据类型是地址。p是变量名,对应的内存空间的大小是sizeof(char *),对应的地址假设是0x002,也就是地址0x002中存放的是变量p的值,存放的数据类型是指针

形参和实参

void fun(int a); /* 这里的a就是形参 */
int main()
{
	int b = 1;
	fun(b); /* b就是实参 */
}

总结一句话:形参是函数定义的时候用的,实参是调用函数的时候用的
函数的参数都是形参,只有在函数调用的时候系统才会为形参分配空间和地址,形参和实参不会是同一个内存地址

函数传参的值传递和地址传递
例:int a = 1; a在内存中的地址假设是0x001
值传递:传递的变量的值,就是传递的是1这个值
地址传递:传递的是变量的地址,就是传递的是0x001这个地址

int型变量的值传递和地址传递

/* 值传递 */
void fun(int b)
{
	b = 1; 
}

int main()
{
	int a = 0;
	fun(a);
	printf("a=%d\n", a);
	return 0;
}

值传递:调用fun函数的时候,系统会先为b分配空间,然后将a的值赋值给b,也就是b的值就等于0了。但是b的地址与a的地址是不同的,只是对应地址中存放的值是相同的。假设a的地址是0x001,b的地址是0x002,然后空间大小都是sizeof(int),0x001(a)地址空间中存放的是0,0x002(b)空间中的存放的值也是0。当b=1的时候,只是将0x002地址对应的空间赋值为1,与0x001地址对应的空间是没有关系的。所以a仍然是0

/* 地址传递 */
void fun(int *b)
{
	*b = 1; 
}

int main()
{
	int a = 0;
	fun(&a);
	printf("a=%d\n", a);
	return 0;
}

地址传递:调用fun函数的时候,b还是形参,只不过变成一个指针变量。假设a地址仍然是0x001,b的地址仍然是0x002。a的地址对应的内存空间中存放的是整型的0,b的地址对应的内存空间中存放的是一个地址,即a的地址0x001。当*b = 1的时候,操作的实际上是0x001这个空间,这个空间对应的就是变量a,所以a的值会变为1

指针变量的值传递和地址传递
说明之前先说一下malloc函数:
malloc:malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址。简单的说就是分配一片内存,返回一个地址,让用户可以通过返回的地址来操作这片内存,我们就称这个地址叫操作地址吧。

/* 值传递 */
void fun(char *b)
{
	if (b == NULL) {
		b = (char *)malloc(10);
	}
}

int main()
{
	char *a = NULL;
	fun(a);
	if (a == NULL) {
		printf("a is null\n");
	} else {
		printf("a not null\n")
	}
	return 0;
}

值传递:a是实参,假设地址是0x001,b是形参,假设地址是0x002,a和b都是一个指针变量,占用内存空间是sizeof(char *)。参考上面说的int型变量的值传递,可以得出a和b只是在值上面是相同的,也就是其内存空间中存放的都是NULL而已。在函数fun中调用malloc,假设malloc分配的内存空间的操作地址是0x003,那么现在指针变量b(0x002)的内存空间中的值就从NULL变成了0x003。与int型变量的值传递一样,0x002地址对应的内存空间的修改与0x001地址的对应内存空间是没有任何关系的,所以a依然还是NULL。

下面说指针变量的地址传递,在地址传递说明之前先看一下二级指针变量的概念:指向指针的指针叫二级指针。简单来说,指针变量是存放地址的变量,不管是一级指针还是二级指针或者多级指针,总结起来都是存放地址的变量。一级指针变量和二级指针变量区别看下面的函数来说明:

int main()
{
	int a = 1;
	int *p = &a;
	int **pp = &p;
}

a是一个整型变量,p是一个一级指针变量,pp是一个二级指针变量。a的内存地址假设为0x001,p假设为0x002,pp假设为0x003。a(0x001)对应内存空间中存放的是整型数值1,p(0x002)内存空间中存放的是a的地址0x001,pp(0x003)中存放的是p的地址(0x002)。所以一级指针变量是其内存空间中存放的地址所对应空间中存放的是一个实际的数值。二级指针变量就是其内存空间中存放的地址所对应的空间中存放的还是一个地址。下面看地址传递

/* 地址传递 */
void fun(char **b)
{
	if (*b == NULL) {
		*b = (char *)malloc(10);
	}
}

int main()
{
	char *a = NULL;
	fun(&a);
	if (a == NULL) {
		printf("a is null\n");
	} else {
		printf("a not null\n")
	}
	return 0;
}

a是一个一级指针变量,假设地址为0x001,存放的是NULL,b是一个二级指针变量的形参,假设地址是0x002。在函数调用的时候将a的地址0x001放置到b(0x002)对应的空间中,也就是b现在存放的是0x001。fun函数中的操作是:*b表示取指针对应地址空间的值,取出来的就是0x001地址,将malloc所分配出来的空间的操作地址,假设是0x003,放置到0x001地址对应的空间中了,也就是地址0x001地址中现在存放的是0x003了。所以通过一级指针变量a也能操作malloc出来的空间了。

为啥free不需要传二级指针
看下面的代码,结果是最终打印的是"a not null",是不是说明这样写无法正确free呢?

void fun(char *b)
{
	if (b != NULL) {
		free(b);
		b = NULL;
	}
}

int main()
{
	char *a = (char *)malloc(10);
	if (a != NULL) {
		fun(a);
	}
	
	if (a == NULL) {
		printf("a is null\n");
	} else {
		printf("a not null\n")
	}
}

a是实参,假设地址是0x001,b是形参,假设地址是0x002,malloc分配出来的空间操作地址假设是0x003。从之前的叙述中已经可以得到,当调用fun函数的时候,a和b地址空间中的值应该是相同的,也就是都是0x003。free(b)也就是释放的是0x003地址所指向的内存空间,这个确实也是我们想要释放的内存空间,所以释放完全没有问题。释放之后b=NULL,这个又回到了指针变量的值传递了,所以在main函数中,打印出来的是"a not null"

总结:如果函数的形参和传入的实参类型一致,就是值传递。如果传入的实参是形参的指针,那就是地址传递

发布了108 篇原创文章 · 获赞 8 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_33242956/article/details/90602788