C语言深入理解指针(3)
二维数组传参本质
不管是一位数组还是二维数组传参,形参可以写成数组,也可以写成指针
这里要对数组名做一个深入的理解
咱们要考虑到arr是数组首元素的地址
数组名+i就是跳过i个数组
arr[i]-----(arr+i)---第i行 arr[i][j]-----((arr+i)+j)---(arr+i)是第i行首元素的地址,+j就表示下标为j的数字的地址,再解引用就能得到下标为j的数字
对于一个二维数组的话,arr是数组名,同时也是这个二维数组第一行的元素地址
arr+i就是这个二维数组第i行的地址
对于(*(arr + i))[j]的理解
*(arr + i)就是这个二维数组的第i行的地址解引用得到第i行
[j]这个就是第i行下标为j的元素
对于((arr + i)+j)的理解
*(arr + i)是第i行首元素的地址,这个首元素的地址+j就是第i行下标为j的数字的地址,
*(arr + i)+j再将其解引用得到的就是第i行下标为j的元素
//二维数组传参的本质
/*void print(int arr[3][5], int r, int c)
{
//两层循环,第一层循环打印行,第二层循环打印列
for (int i = 0; i < r; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);//打印一行的内容
}
printf("\n");//换行
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };
print(arr, 3, 5);//将arr数组的内容打印出来,数组名、行和列都要传上去
return 0;
}*/
//一维数组传参
/*数组名是首元素的地址
* 一维数组在传参的时候,其实传递的是首元素的地址
*
* 函数的参数可以写成数组,也可以写成指针
*
* 那么二维数组呢?
* 二维数组的数组名该如何理解呢?
* 其实二维数组的数组名也是数组首元素的地址,但是是那个数字呢?
二维数组可以理解为一维数组的数组
将二维数组里面的三个一维数组当成一个元素
二维数组有三个元素,每个元素有三个数组
二维数组的每一行可以看成是一个一维数组‘二维数组的首元素就是这个二维数组的第一行
二维数组的数组名就是第一行的地址
不是第一行首元素的地址,而是第一行整个一维数组的地址
*/
void print(int(*arr)[5], int r, int c)//因为传过来的是一位数组的地址,我们要用数组指针接收
//使用数组指针存放第一行的地址,传过来的是一个一维数组,那么我们就需要一个数组指针来接收
{//这个数组指针并不是指向的二维数组,而是指向的是这个二维数组的第一行
//两层循环,第一层循环打印行,第二层循环打印列
for (int i = 0; i < r; i++)
{
for (int j = 0; j < 5; j++)
{
//第一行是arr
//第二行是arr+1
printf("%d ", (*(arr + i))[j]);//arr+i就是第几行,*(arr + i)就是解引用相当于直接访问数组后面的[j]就是那一行一维数组里面的第几个元素
} //printf("%d ", *(*(arr + i)+j))
//arr+i是某一行的地址,将其括起来解引用就是这一行,相当得到第i行的数组名
// *(arr+i)==arr[i],arr[i]是第i行的数组名,数组名是数组首元素的地址
// *(arr + i)数组首元素的地址+j就是这一行首元素后面第j个元素的地址,再解引用
// 再将这个地址解引用得到的就是数组首元素
// *(arr + i)一行首元素的地址再+j就是下标为j的元素了
// 对于这个二维数组,arr[i]表示的是arr[i][0],第i行第1个元素的地址
//
//
//*(arr + i)这个是某一行,相当于拿到这一行的数组名字
//对于二维数组arr的话
//arr[0]是第一行的数组名
//arr[1]是第二行的数组名
printf("\n");//换行
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };
print(arr, 3, 5);//将arr数组的内容打印出来,数组名、行和列都要传上去
//arr是在这个二维数组首元素的地址,是第一行的地址,第一行是一位数组
//这里传过去的是第一行的地址
return 0;
}
/*不管怎么写对于二维数组arr+i就是这个二维数组的第i行一维数组,
* 传参时的arr是二维数组第一行,
*
* 数组名+i就是跳过一整个数组,对于二维数组,里面是存在好几个一维数组的
对于(*(arr + i))[j]的理解
*(arr + i)就是这个二维数组的第i行的地址解引用得到第i行
[j]这个就是第i行下标为j的元素
对于*(*(arr + i)+j)的理解
*(arr + i)是第i行首元素的地址,这个首元素的地址+j就是第i行下标为j的数字的地址,
*(arr + i)+j再将其解引用得到的就是第i行下标为j的元素
arr[i]-----*(arr+i)---第i行
arr[i][j]-----*(*(arr+i)+j)---*(arr+i)是第i行首元素的地址,+j就表示下标为j的数字的地址,再解引用就能得到下标为j的数字
*/
*(*(arr+i)+j)
是访问二维数组中第i
行,第j
列元素的方法。
在C语言中,二维数组可以看作是由多个一维数组组成的连续存储空间。定义一个 int
类型的二维数组 arr
,用 arr[i][j]
表示其第 i
行、第 j
列的元素,也可以写成 *(*(arr+i)+j)
。
解释 *(*(arr+i)+j)
的过程如下:
-
arr
是一个指向二维数组首地址的指针,*(arr+i)
表示访问第i
个一维数组的首地址。 -
*(arr+i)+j
表示在第i
个一维数组的基础上向后移动j
个位置,即访问第i
行、第j
列元素的地址。 -
*(*(arr+i)+j)
表示访问上述地址所存储的值,即获取第i
行、第j
列的元素值。
(*(arr + i))[j]
也可以用来访问二维数组中第 i
行,第 j
列的元素。
在C语言中,二维数组可以看作是由多个一维数组组成的连续存储空间。定义一个 int
类型的二维数组 arr
,用 arr[i][j]
表示其第 i
行、第 j
列的元素,也可以写成 *(*(arr+i)+j)
或 (*(arr + i))[j]
。
解释 (*(arr + i))[j]
的过程如下:
-
arr
是一个指向二维数组首地址的指针,*(arr + i)
表示访问第i
个一维数组的首地址。 -
(*(arr + i))[j]
表示使用[]
运算符访问该一维数组中下标为j
的元素,即获取第i
行、第j
列的元素值。
typedef对指针类型进行重命名
//typedef unsigned int unit;
////用typedef来给类型起别名,我们直接用别名来引用这个类型
//int main()
//{
// unsigned int num;//每次我们写这个无符号整型的时候觉得太麻烦了
////那我们给unsigned起一个小名,利用typedef,创建一个unsigned int 的小名unit
// //当我们想使用unsigned int 的时候我们就直接用小明
// return 0;
//}
//对简单指针类型进行重命名、
//typedef int* pin_t;
//int main()
//{
// int* p;//原来我们写整型指针这么写,现在我们对整型指针进行重命名
// pin_t p2;//同样也是整型指针
//
// return 0;
//}
//
// 对数组指针进行重命名
//typedef int(*parr_t)[6] ;
////对数组指针类型重命名的新名字要放在*旁边,那么现在parr_t就是新的数组指针类型
////新名字一定要放在*旁边
//int main()
//{
// int arr[6] = { 0 };
// int (*p)[6] = &arr;//这里的p就是数组指针
// //对于数组指针,去掉名字就是类型
// //这里的p是数组指针变量的名字
// parr_t p2 = &arr;
// return 0;
//}
//对函数指针进行重命名
int Add(int x, int y)
{
return x + y;
}
typedef int (*pf_t)(int, int);
//同样,把重命名的新名字放在*旁边
int main()
{
int (*pf)(int ,int ) = Add;//去掉名字就是函数指针类型
//int (*)(int, int)函数的指针类型,我们对其进行重命名简单化
pf_t pf2 = Add;
return 0;
}
//由此可得,对于数组指针和函数指针重命名,我们必须把函数的新名字放在指针类型的*旁边
//否则无法重命名成功
针对简单的指针、函数指针、数组指针
首先我们需要这些指针的指针类型,然后再进行重命名,将指针简单化,
函数指针
/*int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pa)(x, y) = &Add;//也可以不加取地址符号,函数名就是函数的地址
//这里的pa是函数指针变量
//这里pa的指针类型是int (*)(x, y)
//去掉名字就是函数指针类型
int ret1=(*pa)(4, 5);//*(pa)--找到函数,然后后面括号内传参
//通过函数指针调用函数
printf("%d", ret1);
int ret2 = pa(4, 5);//打印结果都是一样的,
//可以直接通过函数指针变量的名字来调用函数
//如果要在变量前面加*,必须加括号
printf("%d", ret2);
return 0;
}*/
函数指针数组的创建以及应用
//指针数组
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mull(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
//int* arr1[6];//放的是整型指针---整型指针数组
//char* arr2[6];//放的是字符指针---字符指针数组
//
//创建一个数组,它的每个元素是函数的数组--函数指针数组
//int (*pf1)(int, int)=Add;//加法函数指针
//int (*pf2)(int, int) = Sub;//减法函数指针
//int (*pf3)(int, int) = Mull;//乘法函数指针
//int (*pf4)(int, int) = Div;//除法函数指针
//创建一个函数指针数组来存放这些函数的地址
int (*pf[4])(int, int) = { Add,Sub,Mull,Div };
//在函数在指针变量的基础上来进行函数指针数组的创建
//直接在指针变量名字后面加上:[数组元素数量]={放的元素}
for (int i = 0; i < 4; i++)
{
int ret=pf[i](6,2);//直接访问函数指针数组
printf("%d\n", ret);
}
return 0;
}
//
// 如果这些函数指针的类型是不相同的,是不能放到同一个数组内的
// 数组内的元素都=是相同类型的
转移表---计算器
// 实现计算器
// 完成函数的加法、减法、乘法、除法
//
//int Add(int x, int y)
//{
// return x + y;
//}
//int Sub(int x, int y)
//{
// return x - y;
//}
//int Mull(int x, int y)
//{
// return x * y;
//}
//int Div(int x, int y)
//{
// return x / y;
//}
//void menu()
//{
// printf("**************************************\n");
// printf("********** 1.add 2.sub***********\n");
// printf("********* 3.mull 4.div***********\n");
// printf("********* 0.exit *************\n");
// printf("**************************************\n");
//}
//int main()
//{
// int ret = 0;
// int input = 0;
// int x = 0, y = 0;
// do
// {
//
// menu();//菜单
// printf("请选择:");
// scanf("%d", &input);
// switch (input)
// {
// case 1:
// printf("请输入两个操作数\n");
// scanf("%d %d", &x, &y);
// ret=Add(x, y);
// printf("%d\n", ret);
// break;
// case 2:
// printf("请输入两个操作数\n");
// scanf("%d %d", &x, &y);
// ret = Sub(x, y);
// printf("%d\n", ret);
// break;
// case 3:
// printf("请输入两个操作数\n");
// scanf("%d %d", &x, &y);
// ret = Mull(x, y);
// printf("%d\n", ret);
// break;
// case 4:
// printf("请输入两个操作数\n");
// scanf("%d %d", &x, &y);
// ret = Div(x, y);
// printf("%d\n", ret);
// break;
// case 0:
// printf("退出计算器\n");
// break;
// default:
// printf("选择错误,重新选择\n");
// break;
// }
// } while (input);
//}
//大家有没有觉得很麻烦,假设我们还要其他的计算,比如按位或,按位与异或该怎么办呢?
//难道还是这样吗?一个个添加,运算语句增加,case语句会越来越长了
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mull(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
void menu()
{
printf("**************************************\n");
printf("********** 1.add 2.sub***********\n");
printf("********* 3.mull 4.div***********\n");
printf("********* 0.exit *************\n");
printf("**************************************\n");
}
int main()
{
int ret = 0;
int input = 0;
int x = 0, y = 0;
/*int (*pfarr)(int, int);*///创建一个函数指针
int (*pfarr[5])(int, int) = {0,Add,Sub,Mull,Div};//在函数指针的基础上添加一个括号就是函数指针数组
// 0 1 2 3 4
//专门用来把存放函数指针的
//当我们想调用加法函数的时候,我们直接访问这个数组下标为1
//那么我们就不需要case语句了
do
{
menu();//菜单
printf("请选择:");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("请输入两个操作数:\n");
scanf("%d %d", &x, &y);
ret = pfarr[input](x, y);//加入输入的input是1,就访问这个函数指针数组下标是1的函数
}
//通过数组这一方式直接调用里面的函数进行计算
else if (input == 0)
{
printf("退出计算器\n");
}
else
{
printf("选择错误,请重新选择\n");
}
printf("%d\n", ret);
} while (input);
return 0;
}
//这里的函数指针数组就叫转移表,利用转移表实现我们想要的逻辑
这里的函数指针数组就叫转移表,利用转移表实现我们想要的逻辑
- 点赞
- 收藏
- 关注作者
评论(0)