深入理解C语言系列之函数传参的那些事儿(函数参数、指针、地址、数组)

一、指针与函数之间的关系

1、我们可以给一个函数传一个整型、字符型、浮点型的数据,也可以给函数传入一个地址。

2、函数的传参方式:复制传参(数值)、地址传参(地址值)

3、如果实参是一个普通变量,那么地址传参时,形参就需要使用一级指针;
如果实参是一个一级指针,那么地址传参时,形参就需要使用二级指针;
以此类推…

二、复制传参

#include <stdio.h>

//交换数值的一个函数 
void fun(int a, int b)
{
    
    
	int temp;
	temp= a;
	a= b;
	b= temp;
	
	printf("在函数中数值:a=%d,b=%d\n", a, b);
	printf("在函数中地址:a=%p,b=%p\n", &a, &b);
}

//测试函数 
void test()
{
    
    
	int a=30, b=100;
	printf("传参之前数值:a=%d,b=%d\n", a, b);
	printf("传参之前地址:a=%p,b=%p\n", &a, &b);
	
	fun(a, b);
	
	printf("函数结束后数值:a=%d,b=%d\n", a, b);
	printf("函数结束后地址:a=%p,b=%p\n", &a, &b);	
} 

int main()
{
    
    
	test(); 
} 

测试结果:
01
可以看到,形参a与实参a的地址是不同的,说明形参a与实参a就是两个独立开来的变量,因为作用域的原因,它们可以重名,但实际上是两个不同的变量。
因此,改变形参的值,并不会影响到实参的大小。

三、地址传参

1、传一维指针

#include <stdio.h>

//一个交换地址的函数
void fun2(int *p1, int *p2)
{
    
    
	int temp;
	temp= *p1;
	*p1= *p2;
	*p2= temp;
	
	printf("在函数中数值:*p1=%d,*p2=%d\n", *p1, *p2);
	printf("在函数中地址:p1=%p,p2=%p\n", &p1, &p2);
 } 

//测试函数 
void test()
{
    
    
	int a=30, b=100;
	printf("传参之前数值:a=%d,b=%d\n", a, b);
	printf("传参之前地址:a=%p,b=%p\n", &a, &b);
	
	fun2(&a, &b);
	
	printf("函数结束后数值:a=%d,b=%d\n", a, b);
	printf("函数结束后地址:a=%p,b=%p\n", &a, &b);	
} 

int main()
{
    
    
	test();
} 

测试结果:
02
地址传参的分析方法与复制传参类似,可以看到形参的地址与实参的地址是一样的,说明实参就代表了形参,改变形参的值就是在改变实参的大小。

2、传字符串指针

#include <stdio.h>
void fun(char **q)
{
    
    
	*q= "Oh my love";
}
int main()
{
    
    
	char *p= "hello world";
	fun(&p);
	printf("%s", p); //输出字符串 
} 

03
这同样也是地址传参,因此会改变实参的数据值。

四、传一维数组(地址)

  • 只要是传入一维数组的数组名,都是地址传参,会改变原来数组的数据值。
    方式一:形参用一维数组,int p[]
    方式二:形参用一维指针,int *p
#include <stdio.h>
void fun1(int *p)  //也可以用int p[] 
{
    
    
	p[2]= 520;
	printf("函数内:p[2]=%d\n", p[2]);
	printf("函数内:p[3]=%d\n", *(p+3));  //相当于p[3] 
}
int main()
{
    
    
	int a[10]= {
    
    10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
	fun1(a);
	printf("函数结束后:a[2]=%d\n", a[2]);
	printf("函数结束后:a[3]=%d\n", a[3]);
}

04
因为传递的都是数组名,也就是地址传参了,所以修改形参的值就会改变实参的大小。

五、传二维数组(地址)

传递二维数组名都是地址传参,但是只有使用p[][]数组的方式才能改变原来数组的数据值。

#include <stdio.h>
void fun2(int (*p)[4])  //也可以用p[][4] 
{
    
    
	p[0][2]= 520;  //这样不会改变原数组的数据
	*(*(p+1)+2)= 120; 
	printf("函数内:p[0][2]=%d\n", p[0][2]);
	printf("函数内:p[1][2]=%d\n", *(*(p+1)+2));  //相当于p[1][2] 
} 
int main()
{
    
    
	int a[][3]= {
    
    {
    
    10, 9, 8},{
    
    7, 6, 5}, {
    
    4, 3, 2}};
	fun2(a);
	printf("函数结束后:a[0][2]=%d\n", a[0][2]);
	printf("函数结束后:a[1][2]=%d\n", *(*(a+1)+2));  //相当于a[1][2]
}

06

六、传指针数组(地址)

指针数组本质上就是一个数组,数组里面的每一个元素都是一个函数指针,返回值类型(*函数指针变量名[函数指针的个数])(形参列表);

例如:int(*p[10])(int,int);
定义了一个函数指针数组,有10个元素p[0]~p[9],每个元素都是函数指针变量,指向的函数,必须有整型的返回值,两个整型参数。

测试代码:

void fun3(int **q)
{
    
    
	int i;
	printf("函数内输出:\n");
	*(q)= "You";
	*(q+1)= "are";
	for(i=0; i<3; i++)
	{
    
    
		printf("%s\n", q[i]);	
	}	
} 
int main()
{
    
    
	int i= 0;
	char *p[3]= {
    
    "I", "am", "here"};
	fun3(p);
	printf("函数结束后输出:\n");
	for(i=0; i<3; i++)
	{
    
    
		printf("%s\n", p[i]);	
	}
}

测试结果:
07

七、指针函数和函数指针

指针函数:本质上就是一个函数,是一个返回指针的函数。
函数指针:本质上就是一个指针,是一个指向函数的指针。
详情请参见文章:
C语言指针深入透析(原来你一直没有搞懂C语言指针是因为没有理解其中的规律)

猜你喜欢

转载自blog.csdn.net/Viewinfinitely/article/details/110099208
今日推荐