zoukankan      html  css  js  c++  java
  • C语言的数组,指针,二级指针,指针数组和数组指针的简单理解

    什么是指针

      C语言中的所有变量都是存储在一块内存中的。以32位机器为例,char型的变量存储在一块1字节的内存中,int型的变量存储在一块4字节的内存中。指针本质上也是个变量,也存储在一块4字节的内存中。只不过指针那块内存中存储的是一个地址而已。我们可以把某个变量的4字节地址存储在指针的那块内存中。想要操作变量的时候,先从指针的内存中得到那个变量的地址,然后在根据地址操作对应的那个变量的那块内存。

      int num = 10;

      int *p = #   // 取num的地址存在p指针的内存中

      *p = 100;          // 指针的解引用语法,从指针的内存中的到num的地址,然后操作num对应的内存。相当于num=100

    数组名和指针的区别联系

      int array[10] = {0};

      int *p = array;

      array是数组名,数组第一个元素的地址,是一个常量,不能用于算数运算,array++, array+=1这些运算都是不允许的。指针和它的区别是,指针是一个变量,也就是说,指针的那块内存中你想存啥就存啥。比如 p += 1,,就相当于把指针那块内存中存的值改变了。int *p = array 相当于把array中第一个元素的地址存在指针对应的那块内存中。对数组名使用sizeof,会返回整个数组的大小。但是对指针使用sizeof,只会返回4字节(32位机器)。所以数组名和指针完全是两码事,数组名是常量,而指针本质上是个变量。它们唯一的联系是如下代码中展示的:

      void func(int a[]);

      void func(int *a);

      以上两种声明方式编译器都会把a理解为一个指针,所以这两个声明是一样的。C语言传参方式是按值传递,当把数组名作为参数传进去的时候,因为把数组名直接作为右值会得到第一个元素的地址,所以相当于把数组第一个元素的地址存储到了a对应的内存中。这也是常说的数组名转换为指针的本质。其实和上面的代码: int *p = array 本质上是一样的,都是把数组名代表的地址赋值给了指针。

    数组指针和指针数组

      <1> 数组指针

        int (*p)[3] ;   因为()的优先级高,所以*运算符先把p声明为一个指针。然后后面的方括号和3使编译器把p解释为一个指向长度为3的一维数组的指针。数组指针一般用于指向二维数组如:

        int a[2][3];    // 相当于2个长度为3的一维数组。

        int (*p)[3];

        p = a;

        p++;  // 指针加减是让指针移动sizeof(指针类型)个单位,p的类型指向长度为3的int型一维数组的i指针。是所以p移动了4 * 3字节

       <2>指针数组

        int *p[3];    []优先级高,所以[]先把p声明为一个数组。然后*使编译器把p解释为,p是一个一维数组,其中每个元素的类型是int *。

       这里需要理解清楚,数组指针是一个指针,在32位机器上就是一块4字节的内存,所以只能保存一个地址。在C语言中一般用来指向二维数组。但是指针数组是一个数组,每个元素是一个指针,所以指针数组能用数组的方式保存多个指针,也就是每个数组元素对应内存中存储的其实是某个变量的地址。

    二级指针

      二级指针是指针的指针。说白了,就是二级指针对应的那块内存中存储的是另外一个指针的地址。比如:

      int num1 = 10;

      int num2 = 20;

      int *p1 = &num1;

      int **p2 = &p1;

      *p2 = &num2;    //1

      *p1 = 30;           //2

      **p2 = 40;          //3

      这是,p2对应的那块内存中存储的是p1的地址,而p1那块内存中存储的是num1的地址。*p2代表p1那块内存的地址,所以代码1表示p1那块内存中存储num2的地址。代码1之后,*p1对应内存中的值被改成num2的地址了,所以*p1代表num2对应的那块内存。所以代码2的意思是让num2的那块内存存储30。*p2取到的是p1那块内存的值,也就是num2的地址。**p2代表的是num2对应的那块内存。所以代码2代表把num2的值改为30.

      从这里可以看到,我们没有动p1, 但是p1的指向改变了,这就是二级指针最重要的作用。比如有以下场景:

      int getMem1(int *p)

      {

        p = (int *)malloc(sizeof(int));

      } 

      int getMem2(int **p)

      {

        *p = (int *)malloc(sizeof(int));

      }

      int main()

      {

        int *buf;

        //getMem1(buf);    // 错误,C语言中按值传参,形参p对应的内存在getMem1中存储了新申请内存的起始地址,和buf一点关系都没有。

        getMem2(&buf);    // 正确,在getMem2中,形参p中存储的是指针buf的地址,*p代表的是指针buf对应的那块内存,也就是buf那块内存被赋值为新申请内存的起始地址,在子函数中改变了main函数中buf指针的指向。

      }

      因为一级指针不能再子函数中改变自己的指向,所以只能通过二级指针来改变。这也是二级指针最有用的地方。

    指针数组和二级指针的联系

      int *p[10];

      指针数组中的每个元素是一级指针,如果我想改变某个元素(某个指针)的指向,只需要这样:

        p[0] = (int *)malloc(sizeof(int));

        p[1] = (int *)malloc(sizeof(int));

      二级指针的作用也是改变一级指针的指向,这样就把指针数组和二级指针联系起来了。

      int **p2 = p;

      *p2 = (int *)malloc(sizeof(int));

      *(p2 + 1) = (int *)malloc(sizeof(int));

      和上面的代码效果是一样的。*p2代表指针p第一个元素的那块内存。p2是指针的指针,它指向的类型是int *, 所以p2+1表示移动一个int*的位移,这和p+1移动的原理是一样的。

      

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 交换Easy
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法训练 矩阵乘方
    QT中给各控件增加背景图片(可缩放可旋转)的几种方法
    回调函数实现类似QT中信号机制
    std::string的Copy-on-Write:不如想象中美好(VC不使用这种方式,而使用对小字符串更友好的SSO实现)
  • 原文地址:https://www.cnblogs.com/MyOnlyBook/p/9484745.html
Copyright © 2011-2022 走看看