web-dev-qa-db-ja.com

関数Cでメモリ2D配列を割り当てます

関数の2D配列に動的メモリを割り当てる方法は?私はこの方法を試しました:

int main()
{
  int m=4,n=3;
  int** arr;
  allocate_mem(&arr,n,m);
}


void allocate_mem(int*** arr,int n, int m)
{
  *arr=(int**)malloc(n*sizeof(int*));
  for(int i=0;i<n;i++)
    *arr[i]=(int*)malloc(m*sizeof(int));
} 

しかし、それは機能しません。

14
serhii

[]演算子の precedence*遅延演算子よりも高いため、コードは*arr[i]=(int*)malloc(m*sizeof(int));で間違っています。式*arr[i]では、最初にarr[i]が評価され、その後*が適用されます。必要なのはその逆です(arrを逆参照し、[]を適用します)。

このような括弧を使用してください:(*arr)[i]演算子の優先順位をオーバーライドします。これで、コードは次のようになります。

void allocate_mem(int*** arr, int n, int m)
{
  *arr = (int**)malloc(n*sizeof(int*));
  for(int i=0; i<n; i++)
    (*arr)[i] = (int*)malloc(m*sizeof(int));
} 

上記のコードで何が起こるかをさらに理解するには、 this answer を読んでください。

作業が完了したら、動的に割り当てられたメモリの割り当てを常に明示的に解除することが重要です。上記の関数によって割り当てられたメモリを解放するには、これを行う必要があります。

void deallocate_mem(int*** arr, int n){
    for (int i = 0; i < n; i++)
        free((*arr)[i]);
    free(*arr); 
}

さらに、2D配列を作成するより良い方法は、次のように、単一のmalloc()関数呼び出しで 連続したメモリを割り当てる にすることです。

int* allocate_mem(int*** arr, int n, int m)
{
  *arr = (int**)malloc(n * sizeof(int*));
  int *arr_data = malloc( n * m * sizeof(int));
  for(int i=0; i<n; i++)
     (*arr)[i] = arr_data + i * m ;
  return arr_data; //free point
} 

このメモリの割り当てを解除するには:

void deallocate_mem(int*** arr, int* arr_data){
    free(arr_data);
    free(*arr);
}

2番目の手法ではmallocが2回しか呼び出されないため、割り当て解除コードでは、freeがループで呼び出されるのではなく、2回しか呼び出されないことに注意してください。したがって、この手法の方が優れているはずです。

25
Grijesh Chauhan

配列のサイズを変更する必要がない場合(できますが、ilはもう少し複雑になります)、Cで2D配列を作成する簡単で効率的な方法があります。

http://c-faq.com/aryptr/dynmuldimary.html をご覧ください。

2番目の方法(array2と呼ばれる配列の場合)は非常に単純で、苦痛が少なく(mallocsの戻り値のテストを追加してください)、さらに効率的です。

200x100の配列に対して、100000回の割り当てと割り当て解除のベンチマークを行いました。

  • 方法1:1.8秒
  • 方法2:47ミリ秒

そして、配列内のデータはより連続しているため、速度が向上する可能性があります(この方法で割り当てられた配列をコピー、リセットするより効率的な手法が得られる場合があります)。

3

多くの異なるブロックにメモリを割り当てるのではなく、メモリの連続したブロックにこれを割り当てることができます。以下をせよ:

int** my2DAllocation(int rows,int columns)
{
   int i;
   int header= rows *sizeof(int *);
   int data=rows*cols*sizeof(int);
   int ** rowptr=(int **)malloc(header+data);
   if(rowptr==NULL)
   {
      return NULL:
   }
   int * buf=(int*)(rowptr+rows);
   for(i=0;i<rows;i++)
   {
      rowptr[i]=buf+i*cols;
   } 
   return rowptr;
}
2
Smit Patel

これを考慮してください:ただ単一の割り当て

int** allocate2D(int m, int n)
{
    int **a = (int **)malloc(m * sizeof(int *) + (m * n * sizeof(int)));

    int *mem = (int *)(a + m);

    for(int i = 0; i < m; i++)
    {
        a[i] = mem + (i * n);
    }

    return a;
}

無料に:

free(a);
2
TheMan

これは、配列にスペースを割り当てる不必要に複雑な方法です。このことを考慮:

int main(void) {
    size_t m = 4, n = 3;
    int (*2D_array)[m];
    2D_array = malloc(n * sizeof *2D_array);
    free(2D_array);
    return 0;
}
1
autistic

メモリを2次元配列に割り当てるために、次のコードを試しました。

    #include<stdio.h>
    #include<malloc.h>
    void main(void)
    {
    int **p;//double pointer holding a 2d array
    int i,j;
    for(i=0;i<3;i++)
    {
    p=(int**)(malloc(sizeof(int*)));//memory allocation for double pointer
    for(j=(3*i+1);j<(3*i+4);j++)
    {
    *p = (int*)(malloc(sizeof(int)));//memory allocation for pointer holding integer array
    **p = j;                  
    printf(" %d",**p);//print integers in a row 
    printf("\n");
    p++;
    }
    }
    }

上記のコードの出力は次のとおりです。

1 2 3

4 5 6

7 8 9

ポインタの観点から2次元配列を理解するには、メモリにどのように割り当てられるかを理解する必要があります。次のようになります。

                1    2    3
    1000 -->   100  104  108

                4    5    6
    1004 -->   200  204  208

                7    8    9
    1008 -->   300  304  308 

上記から、ダブルポインターであるポインターpにメモリを割り当てると、整数の配列を指しているので、この例では0x1000がポインターpであることがわかります。

このポインターは整数の配列である整数ポインター* pを指しています。メモリが内部forループ内に割り当てられている場合、最初の反復中のポインターは0x100で、** p = jを割り当てるときに整数値1を指しています。同様に、ループの次の反復で2と3を指します。

外側のループの次の繰り返しの前に、次の繰り返しの中でダブルポインターがインクリメントされます。この例に見られるように、ポインターは現在0x1004にあり、整数4,5,6の配列である整数ポインターを指します。ループの次の反復のために。

0
Nanobrains