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移动的原理是一样的。

      

  • 相关阅读:
    网页中防拷贝、屏蔽鼠标右键代码
    Enterprise Library Exception Handling Application Block Part 1
    vs debug 技巧
    Winforms:消除WebBrowser的GDI Objects泄露
    WinForm窗体之间交互的一些方法
    TableLayoutPanel用法
    Winforms:把长ToolTip显示为多行
    Winforms: 不能在Validating时弹出有模式的对话框
    Winforms: 复杂布局改变大小时绘制错误
    读取Excel默认工作表导出XML
  • 原文地址:https://www.cnblogs.com/MyOnlyBook/p/9484745.html
Copyright © 2011-2022 走看看