C语言函数调用和变量、结构体、数组的传参问题

刚刚接触C语言的人可能会函数调用时传参感到疑惑:有的人会认为变量被传入了一个函数,那么在函数里对参数的修改将直接反映在变量本身上,即“把变量本身传给了函数”;有的人虽然能明白函数传参时传的不是变量本身,但是对“传值引用”和“传参引用”却是混淆的。追根到底,还是对C语言中堆、栈、内存分区等不了解,没有计算机体系结构相关知识。

  1. C语言函数调用的本质

C语言调用函数时,从当前栈顶上为调用函数分配了栈空间,栈空间中的内容仅调用函数可访问,且一旦调用函数执行完成,这一块栈空间将被回收(栈指针往栈底方向移动)
栈

举例来感受一下函数调用时栈的使用情况:

int funcA(int a,int b)
{
	a=1;
	b=2;
	return a+b;
}

void main(void)
{
	int a1=0,b1=0;
	funcA(a1,b1);
	return;
}

main函数调用funcA时会首先在栈顶开辟两个int位置,分别把a1和b1的值存入,接着程序执行指针将跳转到funcA的起始地址,开始执行funcA后,funcA会默认此时栈顶往后的第一个int是自己的b,第二个int是自己的a,以此完成了参数传递;到了这里第一个疑问就有解了,有人会觉得把a1传入funcA当做a,那么funcA中对a的修改也会反映在a1上,其实通过以上过程可以看到,a和a1并不是一片地址,a只是在函数调用初期获取了a1的值而已,funcA中对a的修改,实际上不影响a1的值,并且一旦funcA执行完return,栈指针将重新退回到b1所在位置,a、b及以后的内存都变成了未占用空间。
传值引用和传地址引用,举例:

int funcA(int a,int * b)
{
	a=1;
	*b=2;
	return a+(*b);
}

void main(void)
{
	int a1=0,b1=0;
	funcA(a1,&b1);
	return;
}

这里调用funcA时依然是上面的套路,在栈顶开辟空间,但是对于变量b,放的值是b1的地址,同时funcA里*b对b1的地址解引用,直接操作了b1内存,因此funcA执行完成后,b1的值实际上变成了2;但这和“直接把b1本身传递给funcA”还是不一样的,实际上编程语言中没有“将变量本身传递给谁谁”的概念,新手也不要建立起这种思想,传递了地址就是传递了地址。

  1. 结构体作为函数参数

好多人对C语言中结构体的赋值感到困惑,尤其是纠结于所谓的深拷贝和浅拷贝,其实C语言中结构体的赋值机制十分简单:C语言中结构体整体赋值时,是将源结构体的整块内存中的内容复制下来,覆盖到目标结构体的内存上。记住这个机制,好多问题就迎刃而解。
既然结构体能够直接赋值,那么就能够作为函数参数或返回值,这一点和普通变量一样。即使结构体重包含指针、数组等等,都无所谓。

  1. 一维数组作为函数参数

C语言中数组是不能直接赋值的,比如:

int a[10]={0};
int b[10]={2};
a=b;//这句的本意可能是把b的10个元素值赋值给a的是个元素,但是这么写是达不到目的的,这句的意思是把b的值赋给a;在编译阶段就会报错,因为a作为数组名,是只读的,不能被修改。

想要将数组传递给函数就只能是传递数组的地址,数组在内存中占连续空间,只需要把数组的首地址传递给函数即可,传递时不带有数组的大小信息,但是可以在函数参数中另取一个参数专门用于传递数组大小;
函数内部对数组各元素的索引,实际上是通过保存了数组首地址的指针索引的,例如int a存储了数组的首地址,查找数组的第六个元素,a[5]实际上是(a+5),即先对指针a加5,查找到第六个元素的地址,再用*解引用;
这里不得不提一下指针的加减法,在对指针变量加1时,实际上意思是“下一个元素的地址”,这就需要根据指针变量所指向的变量类型决定指针的值真正加多少,例如int * p;int型在32位系统里占4个字节,因此执行p+1实际上是对p的值加4,p原来指向0x8574内存地址,p+1后p将指向0x8578内存。
指针加减法的特殊性决定了我们在把指针传递给函数时必须明确告知函数,这个指针所指的类型是什么,否则将影响函数对指针后续内容的索引。
一维数组作为函数参数时,可以写成funcA(int *a,int a[],int a[10]),三种写法都是等价的;

  1. 二维数组作为函数参数

首先二维数组是一维数组的数组,int a[10][3]在内存上反映为:

二维数组
上文提到,把指针传递给函数时必须指明指针所指向的类型,二维数组也一样,传递二维数组时,可以不告诉函数数组的第二维有多少元素,但是必须告诉函数数组的第一维有多少元素,否则像a+1跳转到第二个一位数组首地址这种操作是做不到的,当然还有元素的类型。
二维数组传参可以写成funcA(int (*a)[3],int a[][3],int a[10][3]),三种写法等价。注意int (*a)[3]千完不要写成int *a[3],这是两个不一样的东西,这里不详细介绍。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值