【C语言进阶】数组、指针、函数——传参篇

目录

写在前面

一、数组传参

一维数组传参

二维数组传参

二、指针参数

一级指针传参

二级指针传参

三、函数传参

四、看懂代码

写在最后


写在前面

上篇文章介绍了指针数组和数组指针,有兴趣的小伙伴可以看一看。

在写程序时,有时难免要把数组或指针传给函数,我们又该怎么写参数呢?开门见山,有两种方法:

  1. 照着原函数写
  2. 用指针接收

一、数组传参

一维数组传参

法一:test1和test2的前两项是照着原函数写

法二:test1和test2的第三项是用指针接收

#include<stdio.h>
void test1(int arr[])//正确
{}
void test1(int arr[10])//正确
{}
void test1(int* arr)//正确
{}
void test2(int* arr[])//正确
{}
void test2(int* arr[10])//正确
{}
void test2(int **arr)//正确,用来存放一级指针变量的地址
{}
int main()
{
	int arr1[10]={0};
	int* arr2[10]={0};
	test1(arr1);
	test2(arr2);
	return 0;
 } 

上面的6种写法  来接收函数传来的地址  均是正确的。

难点解释:

第一种理解方式:test2 中 arr2是首元素地址,也就是一个整形指针的地址,我们当然可以用一个二级指针来接受一级指针的地址,

另一种理解方式:int** arr2  ,写参数时可以理解成 (int*) (*arr2),前一部分可以理解成原函数的类型,后一部分可以理解成一个新命名的指针。

知识补充:

test1函数中,传来的是数组的首元素地址,所以我们可以定义int arr[ ],int arr[一个数字],数组大小可以不写,因为在32位平台,数组首元素大小,也就是指针大小固定4个字节,写不写都没有意义,但写的话就不要写错,以免引起误导。


二维数组传参

法一:第一个test是照着原函数写

法二:后面4个是用指针接收

#include<stdio.h>
void test(int arr[3][5])//正确 
{}
void test(int *arr)//错误,整形指针不能接收数组的地址 
{}
void test(int* arr[5])//错误,指针数组 
{}
void test(int (*arr)[5])//正确,数组指针 可以存放数组的地址 
{}
void test(int **arr)//错误,用来存放一级指针变量的地址 
{}
int main()
{
	int arr[3][5]={0};
	test(arr);
	return 0;
 } 

知识补充:

二维数组的参数中,第一个行参数可以不写,但第二个列参数必须写!

二、指针参数

一级指针传参

#include<stdio.h>
void test(int* p)//正确 
{}
int main()
{
	int a=0;
	int* p=&a;
	test(&a);//正确 
	test(p);// 正确 
	return 0;
 } 

把int 类型改为 char 类型亦是如此


二级指针传参

有问有答:形参是二级指针,能传什么实参呢?

  1. 一级指针变量的地址
  2. 二级指针变量本身
  3. 存放一级指针的数组名,也就是指针数组名
#include<stdio.h>
void test(int **p)
{}
int main()
{
	int a=0;
	int* p=&a;
	int** pp=&p; 
	test(&p);
	test(pp); //两个test等价 
	return 0;
 } 
 

三、函数传参

基本格式:函数返回类型+指针变量+函数类型

#include<stdio.h>
int add(int x,int y)
{
	int z=0;
	z=x+y;
	return z;
}
int main()
{
	int a=3;
	int b=5;
	printf("%d",add(a,b));
	return 0;
}

//上面的函数是普通的传参,下面是与其对照的函数传参

#include<stdio.h>
int add(int x,int y)
{
	int z=0;
	z=x+y;
	return z;
}
int main()
{
	int a=3;
	int b=5;
	int (*p)(int,int)=add;
	printf("%d",(*p)(3,5));
	return 0;
 } 

难点解析:

  • int* p(int,int)=add;  如果我们这样写,就是这样理解的:p是函数名(int ,int)表示add函数的两个整形参数,int* 就是返回类型,这显然与我们的预期,用指针存放add函数的地址,有出入。
  • int (*p)(int,int)=add;用来接收函数地址,其基本格式为函数返回类型+指针变量+函数类型=函数名
  1. (*p)就可以表明p是指针变量。
  2. (int,int)表示add函数的两个整形参数。
  3.    int 表示add函数的返回类型。
  • (*p)(3,5)  是对函数的调用: p存放函数地址,*p就是add函数,等价于add(3,5)

add(3,5)      ⇔       (*p)(3,5)      ⇔      p(3,5)      ⇔ (**p)(3,5)  实际上*再多就没有意义了

我们进行一个简单应用:打印结果为hello world

#include<stdio.h>
void print(char* str)
{
	printf("%s",str);
}
int main()
{
	void (*p)(char*)=print;
    (*p)("hello world");
	return 0;
 } 

四、看懂代码

细细理解下面的代码你会对指针有更加深刻的理解,不了解函数指针基本格式的,可以再看看上面。

一、(*(void(*)())0)()——调用0地址处的该函数(无参无返回类型)

拆分:(*   (  void(*)() )0  )     ()       

  •  void(*)()  是函数指针类型
  •  ()0  是强制类型转换,例如(int)5.0,就是5,在这里就是将0强制类型转换为函数地址
  • *(  void(*)() )0    0既然是地址,那么*解引用之后,就找到了函数
  • 最后面的()是对函数的调用,并且没有传参

翻译解释:把0强制类型转换为一个函数指针类型,该指针指向哪个函数是无参返回类型是void,当0变成一个函数地址之后,对它进行解引用操作,去调用以0为地址处的该函数。


二、void(* signal(int, void(*)(int) ) )(int)

拆分:void(* signal(int, void(*)(int) ) )(int)

signal(参数一, 参数二),随后再把这一部分套进函数参数中就完成复原。

翻译解释:void(* signal(int, void(*)(int) ) )(int)  大概意思就是自定义函数signal里面定义两个参数,一个为int类型,一个为函数指针类型(该函数指针指向的函数的参数为int,返回类型是void), signal函数的返回类型也是一个函数指针,该函数指向的函数参数为int,返回类型为void

写在最后

创作不易,还希望各位大佬支持!

👍🏻点赞,你的认可是我创作的动力!

⭐收藏,你的青睐是我努力的方向!

✏️评论,你的意见是我进步的财富!

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

畅游星辰大海

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

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

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

打赏作者

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

抵扣说明:

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

余额充值