【问题标题】:Single dynamic allocation for multi-dimensional array多维数组的单次动态分配
【发布时间】:2016-07-28 01:30:21
【问题描述】:

基本问题是:

对于需要 指向指针的指针 的代码,该指针将像二维数组一样在语法上被索引,是否有 有效 方法来使用单个分配创建这样的数组?†

从表面上看,我似乎在询问如何做到这一点(如在this question 中),但我已经了解如何做到这一点(见下文)。问题是可能存在对齐问题。‡

文本的其余部分描述了一些替代方案,然后解释了所讨论的方法。


†​​ Olaf 指出指向指针的指针不是二维数组。问题的前提是 3rd 方代码期望传入一个指向指针的指针,3rd 方代码会将其索引为二维数组。

‡ ErikNyquist 提出了一个可能的副本,它解释了如何执行这种分配,但我质疑该技术在数据对齐方面的有效性。


如果我需要动态分配一个多维数组,我通常会使用一个分配调用来避免以后想要释放数组时的迭代。

如果 VLA 可用,我可能会这样编码:

int n, m;
n = initialize_n();
m = initialize_m();
double (*array)[m] = malloc(n * sizeof(*array));

array[i][j] = x;

在没有 VLA 的情况下,我要么依赖结构上的宏来访问数组,要么为指针表添加空间以用于需要二维数组的 ** 样式的代码。宏观方法如下所示:

struct array_2d {
    int n, m;
    double data[];
};

// a2d is a struct array_2d *
#define GET_2D(a2d, i, j) (a2d)->data[(i) * x->n + (j)]

struct array_2d *array = malloc(sizeof(*array) + n * m * sizeof(double));
array->n = n;
array->m = m;

GET_2D(array, i, j) = x;

指针表方法比较复杂,因为它需要一个循环来初始化表。

struct array_p2d {
    int n, m;
    double *data[];
};

#define GET_P2D(a2d, i, j) (a2d)->data[i][j]

struct array_p2d *array = malloc(sizeof(*array) + n * sizeof(double *)
                                 + n * m * sizeof(double));
for (k = 0; k < n; ++k) {
    array->data[k] = (double *)&array->data[n] + k * m;
}

GET_P2D(array, i, j) = x;
// array->data can also be passed to a function wanting a double **

指针表方法的问题是可能存在对齐问题。只要数组的任何类型没有比指针更严格的对齐要求,代码就可以工作。

以上预期总是有效的吗?如果没有,是否有一种有效的方法来为 pointer to pointer 样式的二维数组实现单次分配?

【问题讨论】:

  • int ** 之类的东西不是也不能是二维数组(对于更高维度也是如此)。它也可以不指向一个。所以你的问题毫无意义。使用现代 C 编译器,您将拥有 VLA。不要试图骑死马,这只会导致挫败感——充其量。
  • 看起来同一个问题的答案在这里stackoverflow.com/questions/8740195/…
  • @ErikNyquist 我的问题是关于指针表后面的数组项的对齐。
  • @Olaf:VLA 在 C.99 中是必需的,但在 C.11 中变为可选。显然,我说的不是严格定义的二维 C 数组,而是一个可以像二维一样被索引的指针。
  • @jxh:我没有说使用 C11,而是使用 现代 C 编译器,这是有充分理由的。将强制性(且非常有用的)功能设为可选是委员会最糟糕的决定。这绝对违反了委员会的普遍做法,它甚至仍然允许省略函数声明(它们必须生成警告,但它没有错误)或仍然允许通过 char * 进行别名 - 尽管 void * 等。某编译器供应商的不良影响,超过 17 年仍然无法提供兼容的编译器。

标签: c pointers multidimensional-array malloc


【解决方案1】:

好吧,您的malloc 分配保证是对齐的(除非您使用非标准对齐),所以您需要做的就是将指针表大小四舍五入到数据段的对齐:

const size_t pointer_table_size = n * sizeof(double *);
const size_t data_segment_offset = pointer_table_size +
    ((_Alignof(double) - (pointer_table_size / _Alignof(double))) % _Alignof(double));
double **array = malloc(data_segment_offset + (n * m * sizeof(double));
double *data = (double **)(((char **) array) + data_segment_offset);
for (int i = 0; i != n; ++i)
    array[i] = data + (m * i);

【讨论】:

    猜你喜欢
    • 2017-03-18
    • 2020-04-24
    • 2011-05-31
    • 1970-01-01
    • 2013-04-02
    • 2012-10-07
    • 1970-01-01
    • 2013-11-24
    相关资源
    最近更新 更多