代码之家  ›  专栏  ›  技术社区  ›  Chris Cooper

多维数组在内存中是如何格式化的?

  •  148
  • Chris Cooper  · 技术社区  · 15 年前

    在C中,我知道我可以使用以下代码在堆上动态分配二维数组:

    int** someNumbers = malloc(arrayRows*sizeof(int*));
    
    for (i = 0; i < arrayRows; i++) {
        someNumbers[i] = malloc(arrayColumns*sizeof(int));
    }
    

    很明显,这实际上创建了一个指向一组单独的一维整数数组的一维指针数组,“系统”可以在我要求时理解我的意思:

    someNumbers[4][2];
    

    但是当我静态地声明一个2D数组时,如下面一行所示…:

    int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];
    

    …是在堆栈上创建了类似的结构,还是完全是另一种形式(i、 它是一个一维指针数组吗?如果没有,它是什么,以及如何找到它的引用?)

    还有,当我说“系统”的时候,到底是什么原因造成的?内核?或者是C编译器在编译的时候整理出来的?

    5 回复  |  直到 12 年前
        1
  •  153
  •   Greg Kennedy    9 年前

    一个静态的二维数组看起来像一个数组数组-它只是在内存中连续地排列。数组和指针不是一回事,但是因为你经常可以互换使用它们,所以有时会让人困惑。不过,编译器会正确地跟踪,这会使所有内容都很好地对齐。您必须小心处理您提到的静态2D数组,因为如果您尝试将一个数组传递给一个接受 int **

    int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};
    

    在记忆中是这样的:

    0 1 2 3 4 5
    

    确切地

    int array2[6] = { 0, 1, 2, 3, 4, 5 };
    

    但如果你想通过 array1 对于此函数:

    void function1(int **a);
    

    warning: passing argument 1 of ‘function1’ from incompatible pointer type
    

    . 可以说,数组自动衰减为指针只能达到“一级深度”。您需要将函数声明为:

    void function2(int a[][2]);
    

    void function2(int a[3][2]);
    

    让一切都快乐。

    同样的概念延伸到 N -维度数组。不过,在应用程序中利用这种有趣的业务通常只会使您更难理解。所以在外面要小心。

        2
  •  86
  •   mk12    12 年前

    答案是基于这样一个想法,即C实际上 2D数组-它有数组的数组。当您申报时:

    int someNumbers[4][2];
    

    你在要求 someNumbers 是一个由4个元素组成的数组,其中该数组的每个元素都是 int [2] int s) 是的。

    另一个难题是数组总是在内存中连续排列。如果您要求:

    sometype_t array[4];
    

    那就一直这样:

    | sometype_t | sometype_t | sometype_t | sometype_t |
    

    (4 sometype_t 数组的数组,如下所示:

    | int [2]    | int [2]    | int [2]    | int [2]    |
    

    以及每个 元素本身是一个数组,如下所示:

    | int        | int        |
    

    总的来说,你会发现:

    | int | int  | int | int  | int | int  | int | int  |
    
        3
  •  29
  •   ollo    12 年前
    unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};
    

    在内存中等于:

    unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};
    
        4
  •  5
  •   Jon L    15 年前

    在静态分配数组的情况下,“系统”将是编译器。它将像为任何堆栈变量一样保留内存。

    编译器总是按照声明的方式来处理类型,除了在Carl给出的示例中,它可以找出可互换的用法。这就是为什么如果将[][]传递给函数,它必须假定它是静态分配的平面,其中**假定为指针到指针。

        5
  •  2
  •   sqr163    5 年前

    假设我们有 a1 a2 定义和初始化如下(c99):

    int a1[2][2] = {{142,143}, {144,145}};
    int **a2 = (int* []){ (int []){242,243}, (int []){244,245} };
    

    是在内存和表达式中具有纯连续布局的齐次二维数组 (int*)a1 计算为指向其第一个元素的指针:

    a1 --> 142 143 144 145
    

    a2级 从异类2D数组初始化,是指向类型为的值的指针 int* ,即解引用表达式 *a2 计算为类型的值 内景* ,内存布局不必连续:

    a2 --> p1 p2
           ...
    p1 --> 242 243
           ...
    p2 --> 244 245
    

    尽管内存布局和访问语义完全不同,但数组访问表达式的C语言语法对于同构和异构2D数组看起来完全相同:

    • 表达 a1[1][0] 将获取价值 144 a1级
    • 表达 a2[1][0] 将获取价值 244 数组

    编译器知道 a1级 操作类型 int[2][2] a2级 操作类型 int** . 生成的汇编代码将遵循同构或异构访问语义。

    当数组类型为 int[N][M] 是类型铸造,然后作为类型访问 内景** ,例如:

    ((int**)a1)[1][0]   //crash on dereference of a value of type 'int'
    
        6
  •  1
  •   AlphaGoku    9 年前

    要访问特定的2D数组,请考虑数组声明中的内存映射,如下面的代码所示:

        0  1
    a[0]0  1
    a[1]2  3
    

    要访问每个元素,只需将感兴趣的数组作为参数传递给函数即可。然后使用offset for column分别访问每个元素。

    int a[2][2] ={{0,1},{2,3}};
    
    void f1(int *ptr);
    
    void f1(int *ptr)
    {
        int a=0;
        int b=0;
        a=ptr[0];
        b=ptr[1];
        printf("%d\n",a);
        printf("%d\n",b);
    }
    
    int main()
    {
       f1(a[0]);
       f1(a[1]);
        return 0;
    }