按值调用,模拟按引用调用,动态内存分配函数

按值调用

即用普通变量作函数参数(程序将函数调用语句中的实参的一份副本传给函数的形参,实参的值是不能在被调函数内被修改的)

以两数互换(swap)程序为例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void swap(int a, int b);
int main()
{
	int a, b;
	printf("input a,b:");
	scanf("%d,%d", &a, &b);
	swap(a, b);
	printf("in main():a=%d,b=%d\n", a, b);
	return 0;
}
void swap(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;
	printf("in swwap():a=%d,b=%d\n",a, b);
}

①在主函数中调用swap()函数,先进行实参向形参的单向传递,②在swap()函数中利用临时变量将形参x和y的值互换,③当swap()函数执行完毕,程序的控制流程从swap()返回主函数。

①由于形参x,y是局部变量(其在栈上分配内存,在执行函数调用时,系统在栈上为函数内的局部变量及形参分配内存,函数执行结束,自动释放这些内存),则swap()函数执行结束,为其分配的存储空间就被释放(所谓释放内存,其实就是将内存中的值恢复为随机值(即乱码)),②则形参x,y的值变为随机值,且不能在它们作用域之外(每个变量仅在定义它的语句块内有效,且拥有自己的存储空间)去访问它们

③主函数不能去访问x和y,同时形参x,y的值无法反向传给实参,所以主函数中实参a,b的值没有变化

总结:函数的参数传递是"单向的值传递",即只能将实参的值单向传递给形参,而不能反向将形参的值(注意是!!!)传给实参,形参值的改变也不会影响实参,其根本原因是实参和形参占据着不同的存储单元

!!在按值调用时,为了得到函数修改的形参的值,可利用return语句从被调函数返回被修改的形参值(注意返回的应是一个值!!,如果返回的是动态局部变量的地址,输出其中的数据必然为乱码(动态局部变量都是在栈上创建内存的,在函数调用结束后就被自动释放了,释放后的内存中的数据将变成随机数)),但return仅限于从函数返回一个值,需要函数返回多个值时,就要用到模拟按引用调用的方法

模拟按引用调用

(常用的从函数中返回修改了的数据值的方法,即将地址值传递给被调函数的形参,只有得到变量的地址值,才能利用间接寻址方式在函数中改变相应的地址单元中的数据值,那么相应实参所对应开辟空间里的值会改变,但其地址仍不变)

⒈用数组作函数参数(将数组在内存中的首地址传给函数的形参,然后在函数中利用形参得到的数组元素地址值,对数组元素进行间接寻址来修改数组元素值)

以将学生成绩从高到低排序为例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define N 40
int  readscore(int score[]);
void printscore(int score[], int n);
void datasort(int score[], int n);
int main()
{
	int n, score[N];
	n = readscore(score);
	printf("total students are %d\n", n);
	datasort(score, n);//将数组在内存中的首地址传给形参
	printf("sorted scores:\n");
	printscore(score, n);
	return 0;
}
int readscore(int score[])
{
	int i = -1;
	do
	{
		i++;
		printf("input:\n");
		scanf("%d", &score[i]);
	} while (score[i] >= 0);
	return i;
}
void printscore(int score[], int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d\t", score[i]);
	}
}
void datasort(int score[], int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (score[j] > score[i])
			{
				int temp = score[j];
				score[j] = score[i];
				score[i] = temp;
			}
		}
	}
}

使用数组名作为函数的实参,实际上是将实参数组score的首地址传给函数相应的形参,于是形参数组与实参数组共享同一段存储单元,则对形参数组元素值的改变,也就相当于是对实参数组元素值的改变。

⒉用指针变量作函数参数(将变量在内存中的地址值传给函数的形参,从而使函数的形参指向变量的值,函数使用间接寻址运算符*改变形参指向变量的值,相应实参所对应开辟空间里的值会改变,但其地址仍不变)

以两数交换程序为例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void swap(int* x, int* y);
int main()
{
	int a, b;
	printf("input a,b:\n");
	scanf("%d,%d", &a, &b);
	printf("before swap:a=%d,b=%d\n", a, b);
	swap(&a, &b);//将变量的地址传给函数形参
	printf("after swap:a=%d,b=%d\n", a, b);
	return 0;
}
void swap(int* x, int* y)
{
	int temp = *x;
	*x = *y;
	*y = temp;
}

①在主函数中调用swap()函数,先进行实参向形参的单向传递(此时传递的是地址),②在swap()函数中利用临时变量将形参x和y的指向的变量的值互换,但形参接收的地址值没有改变,③当swap()函数执行完毕,程序的控制流程从swap()返回主函数。

实际上是将变量首地址传给函数相应的形参,于是形参与实参共享同一段存储单元(共用同一地址)

总结:函数的参数传递是"单向传递",即只能将变量地址单向传递给形参,而形参不能返回函数中动态分配的内存的首地址给实参(即地址值是不变的),但由于改变相应的地址单元中的数据值,那么相应实参所对应开辟空间里的值也会改变,注意其地址仍不变(即swap()函数执行完后,主函数的相应变量a,b的地址单元中的数据值发生了改变,但变量a,b的地址值不变)

动态内存分配函数

动态分配的内存不会在函数调用结束后被自动释放,必须使用free()才能释放

以输入一个字符串并将其显示在屏幕上程序为例:

使用动态内存分配函数:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
char* getstr(char* s);
int main()
{
	char* ptr = NULL;
	ptr = getstr(ptr);//是ptr指向动态分配的内存的首地址
	puts(ptr);
	free(ptr);//释放ptr指向的动态分配的内存
	return 0;
}
char* getstr(char* s)//返回字符指针的函数
{
	s = (char*)malloc(80);//申请动态分配的内存
	scanf("%s", s);
	return s;//返回动态分配内存的首地址
}

模拟按引用调用(以指针变量作函数形参)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void getstr(char* s);
int main()
{
	char s[80];
	char* ptr =s;//指针初始化,是ptr指向数组s的首地址
	getstr(ptr);//以地址值为实参传递给形参
	puts(ptr);
	return 0;
}
void getstr(char* s)
{
	scanf("%s", s);//地址值不变,改变相应地址单元中的数据值
}

!!下面来展示错误代码(是错误的):

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void getstr(char* s);
int main()
{
	char* ptr =NULL;//指针变量初始化为空指针
	getstr(ptr);//以地址值为实参传递给形参
	puts(ptr);
	return 0;
}
void getstr(char* s)//指针形参接收实参传过来的是空指针
{
	scanf("%s", s);//地址值不变,仍为空指针
}

虽然使用了模拟按引用调用,但是形参s不能返回函数中动态分配的内存的首地址给实参,则实参仍为空指针,于是程序异常终止

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
char *getstr(void);
int main()
{
	char* ptr =NULL;
	ptr=getstr();
	puts(ptr);//试图使用野指针
	return 0;
}
char *getstr(void)
{
	char s[80];//定义动态存储类型的数组
	scanf("%s", s);
	return s;//试图返回动态局部变量的地址
}

在按值调用时,虽然使用了return语句,但返回的不是一个值,而是动态局部变量的地址!!!如果返回的是动态局部变量的地址,输出其中的数据必然为乱码(动态局部变量都是在栈上创建内存的,在函数调用结束后就被自动释放了,释放后的内存中的数据将变成随机数)

附:

一般变量使用取地址符,是获取其地址,即为指向该变量的指针,整个数组是一块连续的内存单元,数组名就是数组的首地址,指向该数组的第一个元素的指针,只不过是常指针而以,则不用再加取地址符。

C语言的字符串为数组形式,故对字符串输入不需要加地址符,而单独的字符,整型变量等的名称不能直接表示地址,故需要加地址符!!

  • 39
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北茉疏影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值