C语言深入理解指针(3)

举报
凯子坚持C 发表于 2024/10/20 18:49:30 2024/10/20
【摘要】 二维数组传参本质不管是一位数组还是二维数组传参,形参可以写成数组,也可以写成指针这里要对数组名做一个深入的理解咱们要考虑到arr是数组首元素的地址数组名+i就是跳过i个数组arr[i]-----(arr+i)---第i行 arr[i][j]-----((arr+i)+j)---(arr+i)是第i行首元素的地址,+j就表示下标为j的数字的地址,再解引用就能得到下标为j的数字对于一个二维数组的...

二维数组传参本质

不管是一位数组还是二维数组传参,形参可以写成数组,也可以写成指针

这里要对数组名做一个深入的理解

咱们要考虑到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) 的过程如下:

  1. arr 是一个指向二维数组首地址的指针,*(arr+i) 表示访问第 i 个一维数组的首地址。

  2. *(arr+i)+j 表示在第 i 个一维数组的基础上向后移动 j 个位置,即访问第 i 行、第 j 列元素的地址。

  3. *(*(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] 的过程如下:

  1. arr 是一个指向二维数组首地址的指针,*(arr + i) 表示访问第 i 个一维数组的首地址。

  2. (*(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;                             
}
//这里的函数指针数组就叫转移表,利用转移表实现我们想要的逻辑

这里的函数指针数组就叫转移表,利用转移表实现我们想要的逻辑

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。