使用malloc分配具有不同行长度的多维数组

ase*_*sel 66 c arrays malloc

我有以下C代码:

int *a;
size_t size = 2000*sizeof(int);
a = (int *) malloc(size);
Run Code Online (Sandbox Code Playgroud)

哪个工作正常.但如果我有以下内容:

char **b = malloc(2000*sizeof *b);
Run Code Online (Sandbox Code Playgroud)

每个元素b都有不同的长度.

怎么可能b像我一样做同样的事情a; 即以下代码是否正确?

char *c;
size_t size = 2000*sizeof(char *);
c = (char *) malloc(size);
Run Code Online (Sandbox Code Playgroud)

Nik*_*sov 77

首先,你需要分配像指针一样的数组char **c = malloc( N * sizeof( char* )),然后用一个单独的调用来分配每一行malloc,可能在循环中:


/* N is the number of rows  */
/* note: c is char** */
if (( c = malloc( N*sizeof( char* ))) == NULL )
{ /* error */ }

for ( i = 0; i < N; i++ )
{
  /* x_i here is the size of given row, no need to
   * multiply by sizeof( char ), it's always 1
   */
  if (( c[i] = malloc( x_i )) == NULL )
  { /* error */ }

  /* probably init the row here */
}

/* access matrix elements: c[i] give you a pointer
 * to the row array, c[i][j] indexes an element
 */
c[i][j] = 'a';
Run Code Online (Sandbox Code Playgroud)

如果您知道元素的总数(例如N*M),则可以在单个分配中执行此操作.

  • 如果在单个操作中分配N*M个字节,则手动填写所有c [i]:c [i] = p + M*i; (2认同)
  • 这取决于c的类型 - 如果它是char**,那么是,如果它是char*,则索引改变:element [i] [j] ~c [i*M + j]. (2认同)
  • @ e19293001是的,每个`malloc`都有一个`free`.你必须循环遍历释放它们的`char*`变量,然后释放`char**`. (2认同)

Joh*_*ode 49

动态分配类型为T的NxM数组的典型形式是

T **a = malloc(sizeof *a * N);
if (a)
{
  for (i = 0; i < N; i++)
  {
    a[i] = malloc(sizeof *a[i] * M);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果数组的每个元素具有不同的长度,则将M替换为该元素的适当长度; 例如

T **a = malloc(sizeof *a * N);
if (a)
{
  for (i = 0; i < N; i++)
  {
    a[i] = malloc(sizeof *a[i] * length_for_this_element);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @Kagaratsch:一般来说,按照你分配的相反顺序释放 - 也就是说,首先释放每个`a [i]`然后释放`a`. (2认同)

Ram*_*esh 28

等效存储器分配char a[10][20]如下.

char **a;

a=(char **) malloc(10*sizeof(char *));

for(i=0;i<10;i++)
    a[i]=(char *) malloc(20*sizeof(char));
Run Code Online (Sandbox Code Playgroud)

我希望这看起来很简单.


小智 10

另一种方法是分配一个连续的内存块,包括用于指向行的指针的头块以及用于在行中存储实际数据的主体块.然后通过将每个行中的内存地址分配给每行的标题中的指针来标记内存.它看起来如下:

int** 2dAlloc(int rows, int* columns) {    
    int header = rows * sizeof(int*);

    int body = 0;
    for(int i=0; i<rows; body+=columnSizes[i++]) {  
    }
    body*=sizeof(int);

    int** rowptr = (int**)malloc(header + body);

    int* buf  = (int*)(rowptr + rows);
    rowptr[0] = buf;
    int k;
    for(k = 1; k < rows; ++k) {
        rowptr[k] = rowptr[k-1] + columns[k-1];
    }
    return rowptr;
}

int main() {
    // specifying column amount on per-row basis
    int columns[] = {1,2,3};
    int rows = sizeof(columns)/sizeof(int);
    int** matrix = 2dAlloc(rows, &columns);

    // using allocated array
    for(int i = 0; i<rows; ++i) {
        for(int j = 0; j<columns[i]; ++j) {
            cout<<matrix[i][j]<<", ";
        }   
            cout<<endl;
    }

    // now it is time to get rid of allocated 
    // memory in only one call to "free"
    free matrix;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是优雅的内存释放和使用类似数组的符号来访问生成的2D数组的元素的能力.

  • 遗憾的是,这也具有不保证非指针大小类型的对齐的副作用.例如:具有32位指针和64位双精度且具有奇数行的系统将在未对齐边界上的"双"开始第一列双精度行.这是非常重要的,因为它可能很容易导致由于数据对齐不当导致的总线错误.一般解决方案应确保数据行从8字节边界开始,产生额外的分配空间,并在将行指针分配给主指针段时进行相应调整. (4认同)
  • 需要注意的是:此解决方案通常在缓存一致性方面表现更好,因为单个行保证是连续的,这与其他一次分配一行的方法不同,并且可能导致其组成部分分散的矩阵整个高度分散的堆. (2认同)