• 二维数组(解引用、指针数组、数组的指针)——C语言


    二维数组

    在说二维数组前先来说下一维数组中的指针数组和和数组的指针

    一、一维数组中指针数组数组指针的区别

    指针数组:

    1 int *p[5];

    []的优先级比*高,首先它是一个数组,它的大小是5,它里面存放的数据类型是int *,也就是整型指针。 所以它叫指针数组,讲到底这个p是一个数组,数组内的元素是5个指针,而数组内的每一个指针指向一个int型的变量

    数组的指针:

    int (*p)[5];

     首先p是一个指针,指向大小为5的数组,因此这叫数组的指针,定义了一个指向5个元素的一维数组的指针。(括号优先)

    二、两者在赋值时的区别

    指针数组的赋值

     1 #include <stdio.h>
     2 
     3 main()
     4 {
     5     int *p[5];
     6     int a = 10;
     7     
     8     p[0] = &a;
     9     printf("%d", *p[0]);
    10 }

    数组的指针的赋值

    1 main()
    2 {
    3     int (*p)[5];
    4     int a[5] = {1, 2, 3, 4, 5};
    5 
    6     p = &a;
    7     printf("%d", *p[0]);
    8 }

    三、关于数组的地址(这里只讨论一维二维数组)

    一维数组

    1 int a[5];

    a表示的是数组的首地址,a等价于&a[0]

    二维数组

    1 int a[2][2] = {1, 2, 3, 4};

    a表示的整个数组的首地址,a[0]表示的是第一行的首地址,这两者者在数值上是一样的,但含义不同(或者说类型不同),数组名a是对于整个数组,a[0]是对于第一行

    对二者(a、a[0])的地址是否相同进行验证

     1 #include <stdio.h>
     2 
     3 int main()
     4 {
     5     int a[2][2] = {1, 2, 3, 4};
     6 
     7     printf("%p
    %p
    ", a, a[0]);
     8 
     9     return 0;
    10 }

    运行结果

    在用数组的地址进行赋值的时候,虽然三者值相同,但是三者不可随意混用(以int a[2][2]为例)

    a--------是int (*)[2]型

    a[0]-----是int *型

    对于a[0]和&a[0][0],两个类型都是int *型的,所以下述两种赋值方法等价

    第一种:

    1 int a[2][2] = {1, 2, 3, 4};
    2 int *p;
    3 p = a[0];

    第二种:

    1 int a[2][2] = {1, 2, 3, 4};
    2 int *p;
    3 p = &a[0][0];

     对于int a[2][2]来说,如果将a[0]改为&a[0],那么&a[0]和a的类型相同,都为int (*)[2]类型,下面以int a[5][5]为例,列出了二维数组的元素在不同方式表达下的不同类型

    也可以用一维指针数组来保存二维数组中某个元素的地址

    1 int a[2][2] = {1, 2, 3, 4};
    2 int *p[2];
    3 p[0] = &a[0][0];
    4 printf("%d", *p[0]);

    四、二维数组的解引用

    以二维数组a[2][3]={1, 2, 3, 4 ,5, 6};为例(第一维是行,第二维是列)

    第一种:*(*a+1)--------等价于a[0][1],因为*的优先级比+高,所以先解引用,进入第二维在第二维里面地址+1,再次解引用得到元素值

    第二种:*(*(a+1))------等价于a[1][0],比上面第一种多加了一个括号,括号优先级最高,先+1移动地址(注意是在第一维里面移动),然后解引用进入第二维,再解引用得到元素的值

    第三种:*(&a[0][0]+1)--等价于a[0][1],这里使用了&取地址符,将原本表示第一个元素的a[0][0]返回到第二个维度,然后第二维地址+1,再解引用得到元素的值

    为方便读者理解下面上图

    补充:

    请读者先看下面的代码

    1 #include <stdio.h>
    2 
    3 main()
    4 {
    5     int a[2][3] = {1, 2, 3, 4, 5, 6};
    6 
    7     printf("%d***%d", *(a[1]+1), (*a+1)[1]);
    8 }

     *(a[1]+1)--------表示的是a[1][1]的值

    (*a+1)[1]--------表示的是a[0][2]的值

    为了方便描述先退回一维数组,以int a[5]来说,a表示的数组a的首地址,a[2]表示在a的基础上移动2个地址(注意a的类型是int *型的,再解引用得到元素的值,意思是a[2]

    实际上包含了两步,第一步地址移动,第二步解引用得到元素的值(注意第二步,有点隐式转换的意思,经常被人忽略)

    现在来解释上面的二维数组就容易多了

    先来看第一个*(a[1]+1),a[1]代表第二行的首地址,注意这里的维度已经是第二维度了,然后括号优先第二维地址+1,最后解引用得到元素的值

    来看第二个(*a+1)[1],这里提一句,因为[]的优先级是比*高的所以这里的括号不能去掉,第一步先解引用进入第二维度(*优先级高于+),然后第二维地址+1,然后再在当前基础上再移动一次地址,最后解引用

    得到元素的值,这里可能有点绕,换个说法就是[1]是在当前维度进行移动,然后解引用(“当前维度”有点不太严谨,为了方便理解先将就这么用了)

    拿a[2][1]来说,一共有四步,其中包含了两次地址移动,两次解引用,执行顺序是:地址移动->解引用->地址移动->解引用(这里提一句,[]的结合性是左结合的,所以在移动的时候先移动行(第一维)再移动列(第二维),小声BB

    详细步骤:第一步:在当前维度地址+2,因为a的维度是第一维,所以是第一维地址+2,即行+2

         第二步:解引用进入第二维度

         第三步:在当前维度地址+1,因为这时已经进入第二维,所以第二维地址+1,即列+1

         第四步:解引用得到元素的值  

  • 相关阅读:
    Flask 框架下 Jinja2 模板引擎高层 API 类——Environment
    java中的URLEncoder.encode对应JS中用decodeURIComponent,js和java编码,解码
    SQL触发器(AFTER和INSTEAD OF)
    jquery对中文进行base64加密,后台用java进行base64解密
    org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [WebApp] in context with path关于数据库库的问题
    Mybatis-Plus代码生成器
    Swagger2-注解说明
    Maven详细配置
    JDK环境变量配置
    内存泄漏测试
  • 原文地址:https://www.cnblogs.com/lanhaicode/p/10366150.html
Copyright © 2020-2023  润新知