指针的进一步应用

    在计算机中,数据是存放在内存单元中的,一般把内存中的一个字节称为一个内存单元。为了更方便地访问这些内存单元,可预先给内存中的所有内存单元进行地址编号,根据地址编号,可准确找到其对应的内存单元。由于每一个地址编号均对应一个内存单元,因此可以形象地说一个地址编号就指向一个内存单元。C 语言中把地址形象地称作**指针**。在 C 语言中,可以使用运算符 & 求某个变量的地址。

指针是有类型的,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。 ​ 指针的大小是固定的4/8个字节(32位平台/64位平台)。

  1. 字符指针 有一种指针类型为字符指针 char* 现有如下代码

    int main()
    {
        //char ch = 'w';
        //char *pc = &ch;
        char* pstr = "hello bit.";
        printf("%s\n", pstr);
        return 0;
    }

    我们此时会对pstr变量有一个疑问,这里是把一个字符串放到pstr指针变量里了吗?

    其实不是的,真正的情况是把字符串 hello bit. 首字符的地址放到了pstr中。 对此我们有一个例题:

    #include <stdio.h>
    int main()
    {
        char str1[] = "hello bit.";
        char str2[] = "hello bit.";
        char *str3 = "hello bit.";
        char *str4 = "hello bit.";
        if(str1 ==str2)
     printf("str1 and str2 are same\n");
        else
     printf("str1 and str2 are not same\n");
           
        if(str3 ==str4)
     printf("str3 and str4 are same\n");
        else
     printf("str3 and str4 are not same\n");
           
        return 0;
    }

    想想输出结果是什么呢?

     

当然经过上述解释,我们也不难得到结论: ​ str1和str2只是首元素的地址,当然不一样,而对于str3和str4来讲,其指向的是同一个常量字符串。C/C++会把常量字符串存储到单独 的一个内存区域, 当几个指针指向同一个字符串的时候,他们实际会指向同一块内存,故其是一样的。

  1. 指针数组 这个很好理解,指针数组就是一个存放指针的数组。

    当然我们经常会把指针数组和数组指针混淆,比如:

    int* arr1[10]; //整形指针的数组
    char *arr2[4]; //一级字符指针的数组
    //[]优先级高于*
    int (*arr3)[10];
    ​

    对于最后一个定义来说,arr3先和*结合,说明arr3是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以arr3是一个指针,指向一个数组,叫数组指针

  2. 数组指针 能够指向数组的指针。 比如:

    int (*p)[10];

    3.1 对于下面的数组:

    int arr[10];

    arr 是什么?而&arr又是什么? arr我们都知道,是数组首元素的地址,那&arr是什么? 我们利用一段代码来理解这个问题:

    include <stdio.h>
    int main()
    {
        int arr[10] = {0};
        printf("%p\n", arr);
        printf("%p\n", &arr);
        return 0;
    }

    在vs运行后会发现:二者的输出值是一模一样的!难道两个是一样的吗?

    在此基础上我们加点东西:

    #include <stdio.h>
    int main()
    {
     int arr[10] = { 0 };
     printf("arr = %p\n", arr);
     printf("&arr= %p\n", &arr);
     printf("arr+1 = %p\n", arr+1);
     printf("&arr+1= %p\n", &arr+1);
     return 0;
    }
    ​

    此时的输出结果便出现了不同:

     

    我们可以清楚的看见,arr+1的地址在arr的基础上只增加了4个字节,这个很好理解,因为数组元素均为int型,arr为首元素地址,+1之后指针向后偏移指向下一个元素,数组在内存中是连续存放的,自然地址增加四个字节。而对于&arr+1,我们看见从010ff6e4变成了010ff708,二者差值转换为十进制即为40,因此&arr即为整个数组的地址。数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40. 3.2 那数组指针是怎么使用的呢?

    我们暂且先不谈一维数组,重点来看二维数组:

    int main()
    {
        int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
        print_arr1(arr, 3, 5);
        //数组名arr,表示首元素的地址
        //但是二维数组的首元素是二维数组的第一行
        //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
        //可以数组指针来接收
        print_arr2(arr, 3, 5);
        return 0;
    }

    首先定义一个二维数组,将数组名以及列行当作参数,要注意的是,二维数组的数组名指的是二维数组的第一行的地址,即一维数组的地址,那对应的函数怎么接收参数呢?

    void print_arr1(int arr[3][5], int row, int col)
    {
        int i = 0;
        for(i=0; i<row; i++)
       {
            for(j=0; j<col; j++)
           {
                printf("%d ", arr[i][j]);
           }
            printf("\n");
       }
    }

    我们可以直接用一个二维数组来接收;

    void print_arr2(int (*arr)[5], int row, int col)
    {
        int i = 0;
        for(i=0; i<row; i++)
       {
            for(j=0; j<col; j++)
           {
                printf("%d ", arr[i][j]);
           }
            printf("\n");
       }
    }

    亦可以用数组指针来接受,要注意此时传过去的数组名指的是二维数组的第一行的地址,因此arr[i] [j]亦可等价于* ( * (arr+i)+j),(arr+i)即可指向某一行,解引用后即为该行首元素地址,再相对应的+j之后再解引用即可得到某个数组元素。

  3. 数组传参和指针传参 4.1 一维数组传参:

    int main()
    {
     int arr[10] = {0};//这是一个数组,元素类型为int
     int *arr2[20] = {0};//这也是一个数组,元素类型为int*
     test(arr);
     test2(arr2);
    }

    对于arr来说,本质传过去的是数组名即首元素的地址,因此在接受参数时有:

    void test(int arr[])
    {}
    void test(int arr[10])//可以用一个数组来接收,这个数组的大小可给也可不给
    {}
    void test(int *arr)//亦可以用一个指针来接收,因为传过来的毕竟本质上还是一个地址
    {}

    而对于arr2来说,其元素种类均为int*,即都为一级指针。有:

    void test2(int *arr[20])//可以用一个指针数组来接收
    {}
    void test2(int **arr)//也可以用一个二级指针来接受,因为元素均为一级指针,其地址应存放在二级指针里面
    {}
    ​

    4.2 二维数组传参:

    int main()
    {
     int arr[3][5] = {0};
     test(arr);
    }

    此时传过去的为一维数组的地址,即一个数组指针,有:

    void test(int arr[3][5])
    {}

    可以用一个二维数组来接收,但是要注意的是行可以省略,列是无论如何都不能省略掉的,因为对一个二维数组,可以不知道有多少行,但是必须知道一行有多少元素

    void test(int (*arr)[5])//
    {}
    ​

    也可以拿一个数组指针来接收,要注意的是,此时用一级指针或二级指针来接收都是不可以的。

    4.3 一级指针传参

    #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.4 二级指针传参

    #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;
    }

    当一个函数的参数部分为二级指针的时候,我们可以用一级指针的地址来接收,也可以用二级指针变量来接收,亦可于用一个指针数组的数组名来接收。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值