• 数组指针、指针数组、二维数组


    数组指针、指针数组、二维数组

    蛋总常说“凡事必有初”,写下这篇博客的初衷在于:
    今天尝试用指针的方式访问一个大二维数组,而又不想用一维数组人工计算偏移量,于是有了指针数组与数组指针的探索。结论是数组指针。

    二维数组

    我们在开辟一个二位数组的时候似乎很简单。
    比如要开辟一个第一二维度分别是5和8的int数组,可以定义为 int a[5][8]。遍历这个数组的方式可以写为:

    int a[5][8];
    for (int i=0; i<5; i++) {
        for (int j=0; j<8; j++) {
            // visit a[i][j]
        }
    }
    

    注意,这个二维数组的第二维是固定的,第一维通常也是固定的,例外情况是 C99 支持的 VLA,可以在函数内声明第一维不定长的数组,如:

    int main() {
        int x = 5;
        int a[x][8];
        return a[2][0];
    }
    

    但是通常我们为了兼容性考虑,不会使用VLA,因为C++不支持VLA。而且VLA只能在函数内声明,无法声明为全局变量,比如以下写法就无法通过编译:

    int x = 5;
    int a[x][8];
    int main() {
        return a[2][0];
    }
    

    数组指针

    数组指针本质还是一个指针,和多维数组搭配使用很方便。
    比如对于上面的二维数组a,可以声明一个对应类型的数组指针 b 为 int (*b)[8], 这里b就是一个数组指针,指向包含8个int元素的一维数组,p++ 会一次跨越8个int元素的步长。若要借助b来访问a的数据,操作如下:

    int a[5][8];
    int (*b)[8];
    b = a;
    for (int i=0; i<5; i++) {
        for (int j=0; j<8; j++) {
            // visit b[i][j]
        }
    }
    

    上面这种方式看起来似乎没有必要,那么当我们需要开辟一个第一维不定长的数组,尤其是全局数组的时候,数组指针就很有必要了。还记得之前说过的 VLA 的局限吗?

    // ...
    int (*b)[8];
    int main() {
        int w = 5;
        b = (int(*)[8])malloc(sizeof(int) * 8 * w);
        for (int i=0; i<5; i++) {
            for (int j=0; j<8; j++) {
                // visit b[i][j]
            }
        }
    }
    

    另外一种使用场景则是在函数传参的时候,使用 int (*p)[8] 作为形参和使用 int p[][8] 作为形参是等价的,做的都是数组指针的工作:

    void foo(int p[][8]) {
        // visit p[i][j]
    }
    void bar(int (*p)[8]) {
        // visit p[i][j]
    }
    int main() {
        int a[5][8];
        foo(a);
        bar(a);
    }
    

    指针数组

    指针数组顾名思义是一个元素类型为指针的数组,本质是个数组。
    指针数组也可以和多维数组搭配使用,通常用于底层维度不确定的情况。当然用于底层维度确定的情况也可以,但是没必要,还是上面的例子:

    int a[5][8];
    int* b[5];
    for (int i=0; i<5; i++) {
        b[i] = a[i];
    }
    for (int i=0; i<5; i++) {
        for (int j=0; j<8; j++) {
            // visit b[i][j]
        }
    }
    

    或者更干脆,当高维长度不知道的时候,我们需要写成如下形式:

    int** b;
    int main() {
        int w = 5;
        b = (int**)malloc(sizeof(int*) * w);
        for (int i=0; i<w; i++) {
            b[i] = (int*)malloc(sizeof(int) * 8);
        }
        for (int i=0; i<w; i++) {
            for (int j=0; j<8; j++) {
                // visit b[i][j]
            }
        }
    }
    

    可以看到,同样是访问数组a,借助数组指针只需要一个指针的空间开销,而借助指针数组则需要5个指针开销。 int (*p)[8] vs. int* p[5] 前者完胜后者。

  • 相关阅读:
    【VS】解决Visual Studio无法登录的问题
    当vue 页面加载数据时显示 加载loading
    form 表单提交的另一种方式 js
    onclick="return doAlert()" onclick="return false"
    vue中sessionStorage的使用
    js把两个对象合并成一个对象
    Oracle 分页查询的一个实例
    oracle Group by 分组查询后,分页
    ProceedingJoinPoint获取当前方法
    Spring AOP无法拦截内部方法调用
  • 原文地址:https://www.cnblogs.com/zhcpku/p/15985540.html
Copyright © 2020-2023  润新知