C语言进阶——2指针(二)

4. 数组参数、指针参数

在写代码时,有时候需要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//正确,传数组名,就是首元素地址,用数组接收,
{}
void test(int arr[10])//正确,这个是说明了接收数组的大小
{}
void test(int* arr)//正确,首元素地址就是指针,这个比较熟悉了
{}
void test2(int* arr[20])//正确,传的是指针数组,用指针数组接收
{}
void test2(int** arr)//正确。指针数组中存放的是一级指针,用二级指针来接收一级指针的地址
{}
int main()
{
	int arr[10] = { 0 };   //整形指针
	int* arr2[20] = { 0 }; //指针数组,存放int*的数组
	test(arr);
	test2(arr2);
}

4.2 二维数组传参

4.2.1 二维数组名和&二维数组名

二维数组名和&二维数组名
在这里插入图片描述

分析
&arr和arr,虽然值是一样的,但是意义不一样。
实际上:
arr表示的是第一行元素的地址,所以arr+1相对于arr的差值是5(一行5个元素)
&arr 表示的是整个二维数组的地址,数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40

4.2.2 二维数组名传参

  1. 二维数组数组名传参,传的虽然是第一行第一个元素的地址,但是表示的意义是第一行元素

分析
在这里插入图片描述

4.2.2 二维数组传参

二维数组传参,函数形参的设计只能省略第一个[ ]的数字。只能省略行不能省略列

void test(int arr[3][5])//正确
{}
void test(int arr[][])//错误 
{}
void test(int arr[][5])//正确
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
void test(int* arr)//错误,这是一个一级指针,不能接收二维数组第一行5个元素
{}
void test(int* arr[5])//错误 ,这是一个一维的整形数组(类型是int*), 不能接收二维数组的第一行的5个元素。要用数组接收的话必须是二维数组接收。
{}
void test(int(*arr)[5])//正确,这是一个指向一个int型的5个元素的数组指针,一个数组指针,
{}
void test(int** arr)//错误,这是一个二级指针(能接收的是一级指针的地址),不能接收传过来的二维数组的第一行的5个元素,
{}
int main()
{
	int arr[3][5] = { 0 };//二维数组,3行5列。
	test(arr);//二维数组名,表示的是二维数组第一行的元素
}

void test(int(*arr)[5]) 数组指针分析
在这里插入图片描述

4.2.3 小结

二维数组的数组名作为函数参数,能用二维数组接收和数组指针接收。

void test(int arr[3][5])//正确
{}
void test(int arr[][5])//正确
{}
void test(int(*arr)[5])

int main()
{
	int arr[3][5] = { 0 };//二维数组,3行5列。
	test(arr);//二维数组名,表示的是二维数组第一行的元素
}

4.3 一级指针传参

4.3.1 一级指针传参,用一级指针接收


#include <stdio.h>
void print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针p,传给函数
	print(p, sz);
	return 0;
}

4.3.2 反向思考

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

void test1(int *p)
{}
//test1函数能接收什么参数?
#include <stdio.h>
void test1(int* p)
{}
int main()
{
	int arr[1] = { 0 };
	int a = 0;
	int* p = &a;
	tast1(&a); //可以传变量地址
	test1(arr);//可以传一维数组数组名
	test1(p);//可以传一级指针
}

变量地址
一维数组数组名
一级指针

4.4 二级指针传参

4.4.1 二级指针传参二级指针接收

#include <stdio.h>
void test(int** ptr)
{
 printf("num = %d\n", **ptr); 
}
int main()
{
 int n = 10;
 int*p = &n;
 int **pp = &p;
 test(pp); //可以传二级指针
 test(&p); //可以传一级指针的地址
 return 0;
}

4.4.1 反向思考

当函数的参数为二级指针的时候,可以接收什么参数?

void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc); //可以传一级指针的地址
 test(ppc);//可以传二级指针
 test(arr);//可以传指针数组的首地址
 return 0;
}

一级指针的地址
二级指针
指针数组的首地址

5. 函数指针

5.1 函数是否有地址——是的

#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

在这里插入图片描述

5.2 保存函数地址的指针——函数指针

5.2.1 如何表示函数指针——类比数组指针

#include<stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	printf("%p\n", &Add);
	int arr[10];
	int (*pa)[10] = &arr; //数组指针的写法是这样的
	int (*pf)(int, int) = &Add; //类比写我们的函数指针  
	      // *pf需要用()括起来,表示这是一个指针,然后后面紧跟的()
	      //表示这是一个函数指针,
	      //参数是int ,int   返回值是(*pf)之前的int
    return 0;
    
}

5.2.2 函数名和&函数名表示意义一样没有区别

#include<stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	printf("%p\n", &Add);
	int arr[10];
	int (*pa)[10] = &arr; //数组指针的写法是这样的


	int (*pf)(int, int) = &Add;    //函数指针&Add和Add表示的意义是完全一样的
	int (* pf)(int, int) = Add;  //取决于你想不想写。
    return 0;
    
}

5.2.3 函数指针如何调用——类比函数调用

#include<stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	printf("%p\n", &Add);
	int arr[10];
	int (*pa)[10] = &arr; //数组指针的写法是这样的

	int (*pf)(int, int) = &Add;    
	//int ret = Add(2, 3);//函数调用是这样使用
	int ret = pf(2, 3);//我们的函数指针和函数名表示的意义一样,类比,我们直接这样调用

	printf("%d\n", ret);

    return 0;
    
}

在这里插入图片描述

5.3 《C陷阱和缺陷》中的两个有趣的代码

5.3.1

 (*(void (*)())0)();
 	//该代码是一次函数调用
    //调用0地址处的一个函数
    //首先代码中将0强制类型转换为类型为void (*)()的函数指针
	//然后去调用0地址处的函数

在这里插入图片描述

5.3.2

void (*signal(int , void(*)(int)))(int);
//该代码是一次函数的声明
//声明的函数名字叫signal
//signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int
///返回类型是void
//signal函数的返回类型是一个函数指针,该函数指针能够指向的那个函数的参数是int,返回类型是void

在这里插入图片描述
代码看起来比较费劲,如何简化:
给函数指针重命名

typedef void(*pf_t)(int) ;//注意这里就是将void(*)(int)重命名为 *pf_t

重命名之后的代码:

pf_t *signal(int , pf_t);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值