【问题标题】:How do we allocate a 2-D array using One malloc statement我们如何使用一个 malloc 语句分配二维数组
【发布时间】:2012-02-03 03:23:22
【问题描述】:

我在一次采访中被问到如何分配一个二维数组,下面是我的解决方案。

#include <stdlib.h>

int **array;
array = malloc(nrows * sizeof(int *));

for(i = 0; i < nrows; i++)
{
    array[i] = malloc(ncolumns * sizeof(int));
    if(array[i] == NULL)
    {
        fprintf(stderr, "out of memory\n");
        exit or return
    }
}

我以为我做得很好,但后来他让我用一个 malloc() 声明而不是两个来做这件事。我不知道如何实现它。

谁能建议我在单个malloc() 中做到这一点?

【问题讨论】:

  • 实际上这是在进行 (nrows + 1) 次 malloc 调用。
  • 警告:这里发布的许多答案都是有害的。阅读 John Bode 和 chux 的答案,忽略其余部分。

标签: c


【解决方案1】:

只需计算nrows 行指针和实际数据所需的内存总量,将它们全部相加,然后进行一次调用:

int **array = malloc(nrows * sizeof *array + (nrows * (ncolumns * sizeof **array));

如果您认为这看起来太复杂,您可以将其拆分并通过命名大小表达式的不同术语使其有点自我记录:

int **array; /* Declare this first so we can use it with sizeof. */
const size_t row_pointers_bytes = nrows * sizeof *array;
const size_t row_elements_bytes = ncolumns * sizeof **array;
array = malloc(row_pointers_bytes + nrows * row_elements_bytes);

然后您需要遍历并初始化行指针,以便每一行的指针指向该特定行的第一个元素:

size_t i;
int * const data = array + nrows;
for(i = 0; i < nrows; i++)
  array[i] = data + i * ncolumns;

请注意,生成的结构与您获得的结构略有不同,例如int array[nrows][ncolumns],因为我们有明确的行指针,这意味着对于这样分配的数组,并没有真正要求所有行具有相同的列数。

这也意味着像array[2][3] 这样的访问所做的事情与对实际二维数组的类似访问不同。在这种情况下,最内部的访问首先发生,array[2]array 的第三个元素中读取一个指针。然后将该指针视为(列)数组的基数,我们在其中索引以获取第四个元素。

相比之下,对于类似的东西

int array2[4][3];

这是一个“打包”的适当二维数组,仅占用 12 个整数的空间,像 array[3][2] 这样的访问简单地分解为向基地址添加偏移量以获取元素。

【讨论】:

  • @Unwind 我可以查看此解决方案的内存图片视图吗?它有助于详细了解此代码。
  • @AmitSinghTomar:我认为我不能用 ASCII 清楚地表示这一点,而且目前没有时间在真正的图形程序中绘制东西,抱歉。也许有人可以介入? :)
  • @AmitSinghTomar 它需要更多的内存,因为每行都有一个指针,这是“隐藏”数组宽度是动态的事实所必需的。通过使array[i] 成为该行数据的有效指针,它允许像array[i][j] 这样的双重索引。
  • 一个问题:int * const data = array + nrows; 行为我生成警告:incompatible pointer types initializing 'int *const' with an expression of type 'int **'; dereference with *。这是要忽略的吗?否则谢谢你一个很好的答案。
  • @OS2 必然会更快(堆分配是一项昂贵的操作),并且可以最大限度地减少每次分配的开销。我确实相信它可以(并且经常会)导致内存中的布局更紧凑,是的,因为新的分配被对齐以适应任何数据,并且这种分配可能比这里的实际数组所需的更浪费。
【解决方案2】:
int **array = malloc (nrows * sizeof(int *) + (nrows * (ncolumns * sizeof(int)));

之所以可行,是因为在 C 语言中,数组只是所有元素一个接一个地作为一串字节。没有元数据或任何东西。 malloc() 不知道它是否分配用作数组中的字符、整数或行。

然后,你必须初始化:

int *offs = &array[nrows]; /*  same as int *offs = array + nrows; */
for (i = 0; i < nrows; i++, offs += ncolumns) {
    array[i] = offs;
}

【讨论】:

  • 感谢 Amigable 的解决方案,但我不明白 int *offs = &arr[nrows] 来自哪里,我的意思是这条线的目的以及我们在哪里声明 &arr[nrows] ??跨度>
  • 添加的设置代码看起来不对,行步长为1,应该是ncolumns。
  • int* 数据​​ = 数组 + nrows;在展开答案与 int* offs = &array[nrows]; 相同@AmitSinghTomar
  • 谢谢@AmigableClarkKant 明白了你的意思 a[i]=*(a+i) ,类似这样的
  • 能否解释一下第二部分?
【解决方案3】:

这是另一种方法。

如果你知道编译时的列数,你可以这样做:

#define COLS ... // integer value > 0
...
size_t rows;
int (*arr)[COLS];
...              // get number of rows
arr = malloc(sizeof *arr * rows);
if (arr)
{
  size_t i, j;
  for (i = 0; i < rows; i++)
    for (j = 0; j < COLS; j++)
      arr[i][j] = ...;
}

如果您使用 C99,则可以使用指向 VLA 的指针:

size_t rows, cols;
...               // get rows and cols
int (*arr)[cols] = malloc(sizeof *arr * rows);
if (arr)
{
  size_t i, j;
  for (i = 0; i < rows; i++)
    for (j = 0; j < cols; j++)
      arr[i][j] = ...;
}

【讨论】:

    【解决方案4】:

    我们如何使用 One malloc 语句 (?) 分配二维数组

    目前没有答案,为真正的二维数组分配内存。

    int **array 是一个pointer to pointer to intarray 不是指向二维数组的指针。

    int a[2][3] 是一个真正的二维数组或array 2 of array 3 of int


    要为真正的二维数组分配内存,使用 C99,使用 malloc() 并保存到指向 variable-length array (VLA) 的指针

    // Simply allocate and initialize in one line of code
    int (*c)[nrows][ncolumns] = malloc(sizeof *c);
    
    if (c == NULL) {
      fprintf(stderr, "out of memory\n");
      return;
    } 
    // Use c
    (*c)[1][2] = rand();
    ...
    free(c);
    

    没有VLA支持,如果维度是常数,代码可以使用

    #define NROW 4
    #define NCOL 5
    int (*d)[NROW][NCOL] = malloc(sizeof *d);
    

    【讨论】:

    • 哇,太梦幻了。
    【解决方案5】:

    你应该可以做到这一点(虽然所有的演员都很难看):

    int** array;
    size_t pitch, ptrs, i;   
    char* base; 
    pitch = rows * sizeof(int);
    ptrs = sizeof(int*) * rows;
    array = (int**)malloc((columns * pitch) + ptrs);
    base = (char*)array + ptrs;
    for(i = 0; i < rows; i++)
    {
        array[i] = (int*)(base + (pitch * i));
    }
    

    【讨论】:

    • 笨拙的代码 - 请参阅 @Amigable 的答案以获得更优雅和可读的解决方案
    • @PaulR:当我发布我的答案时,他还没有更新他的答案,不过我更喜欢 unwind 的答案
    • @Necrolis,我可能因尝试在 80 年代计算机上优化代码而受到损害。 :-) 那时,最好避免乘法内部循环。
    • @Random832:一切都是使用sizeof(int) 的倍数完成的,唯一的对齐问题来自malloc(我对此表示怀疑)。
    • @Necrolis 我的意思是,sizeof(int *)(因此sizeof(int*) * rows)可能不是sizeof(int) 的倍数。在大多数常见平台上是这样,但这并不能保证。就像我说的那样,对于 double 等其他类型来说,这更有可能是一个问题。
    【解决方案6】:

    我不喜欢这种“指向数组的指针数组”来解决多维数组范式。总是偏爱一维数组,在使用数组 [row * cols + col] 访问元素时?将所有内容封装在一个类中并实现“at”方法没有问题。

    如果您坚持使用以下符号访问数组的成员:Matrix[i][j],您可以做一点 C++ 魔术。 @John 解决方案尝试这样做,但他需要在编译时知道列数。使用一些 C++ 并覆盖 operator[],您可以完全做到这一点:

    class Row
    {
    private:
        int* _p;
    
    public:
        Row( int* p )                   { _p = p; }
        int& operator[](int col)        { return _p[col]; }
    };
    
    
    class Matrix
    {
    private:
        int* _p;
        int _cols;
    
    public:
        Matrix( int rows, int cols )  { _cols=cols; _p = (int*)malloc(rows*cols ); }
        Row operator[](int row)       { return _p + row*_cols; }
    };
    

    所以现在,您可以使用 Matrix 对象,例如创建一个乘法表:

    Matrix mtrx(rows, cols);
    for( i=0; i<rows; ++i ) {
        for( j=0; j<rows; ++j ) {
            mtrx[i][j] = i*j;
        }
    }
    

    您现在应该知道优化器正在做正确的事情,并且没有调用函数或任何其他类型的开销。没有调用构造函数。只要您不在函数之间移动矩阵,就不会创建 _cols 变量。语句 mtrx[i][j] 基本上是做 mtrx[i*cols+j] 的。

    【讨论】:

    • 这个问题从来没有C++标签。
    【解决方案7】:

    可以这样做:

    #define NUM_ROWS 10
    #define NUM_COLS 10
    
    int main(int argc, char **argv)
    {
        char (*p)[NUM_COLS] = NULL;
        p = malloc(NUM_ROWS * NUM_COLS);
        memset(p, 81, NUM_ROWS * NUM_COLS);
        p[2][3] = 'a';
        for (int i = 0; i < NUM_ROWS; i++) {
            for (int j = 0; j < NUM_COLS; j++) {
                printf("%c\t", p[i][j]);
            }
            printf("\n");
        }
    } // end of main
    

    【讨论】:

      【解决方案8】:

      您可以使用 malloc 分配 (row*column) * sizeof(int) 字节的内存。 这是一个代码sn-p来演示。

      int row = 3, col = 4;
      int *arr = (int *)malloc(row * col * sizeof(int));
      
      int i, j, count = 0;
      for (i = 0; i <  r; i++)
        for (j = 0; j < c; j++)
           *(arr + i*col + j) = ++count; //row major memory layout
      
      for (i = 0; i <  r; i++)
        for (j = 0; j < c; j++)
           printf("%d ", *(arr + i*col + j));
      

      【讨论】:

      • 用这种方法得到分段错误
      猜你喜欢
      • 2016-12-11
      • 2021-03-05
      • 2021-05-13
      • 2013-05-25
      • 1970-01-01
      • 2015-08-08
      • 2019-06-07
      • 2012-08-10
      • 1970-01-01
      相关资源
      最近更新 更多