4

動的 2D 配列を入力として受け取り、配列を変更しない C 関数を作成したいと考えています。

コードをより明確にするためだけでなく、関数が C++ コード内から呼び出されるため、const を正しくしようとしています。C++ はこれらのことについてかなり熱心です。

ポインタへの 'const' ポインタを取る関数を宣言するにはどうすればよいですか。つまり、関数が 2 次元配列の内容を変更しないことを示すにはどうすればよいですか?

以下は、具体的で非常に単純な例です。サイズ nxn の C の正方行列を表すために double の 2D 配列、つまり double** を使用しています。これらの行列の 1 つのトレースを計算する関数を書きたいと思います。

#include <stdlib.h>
#include <stdio.h>

double **sqr_matrix_new(int n)
{
  double **a = calloc(n, sizeof(double*));
  int i;
  for (i=0; i < n; ++i) a[i] = calloc(n, sizeof(double));
  return a;
}

void sqr_matrix_free(double **a, int n)
{
  int i;
  for (i=0; i < n; ++i) free(a[i]);
  free(a);
}

double sqr_matrix_trace(double **a, int n)
{
  double trace;
  int i;
  for (i=0, trace=0.0; i < n; ++i) trace += a[i][i];
  return trace;
}

double sqr_matrix_trace_const(const double * const *a, int n)
{
  double trace;
  int i;
  for (i=0, trace=0.0; i < n; ++i) trace += a[i][i];
  return trace;
}

int main(int argc, char *argv[])
{
  int n = 10;
  double **a = sqr_matrix_new(n);
  int i, j, k;
  for (i=0, k=0; i < n; ++i){
    for (j=0; j < n; ++j) a[i][j] = k++;
  }
  printf("trace is %g\n", sqr_matrix_trace(a, n));
  printf("trace is %g\n", sqr_matrix_trace_const(a, n));
  printf("trace is %g\n", sqr_matrix_trace_const((const double * const *)a, n));
  sqr_matrix_free(a, n);
}

上記では、トレース関数 sqr_matrix_trace() と sqr_matrix_trace_const() の両方のバージョンがきれいにコンパイルされます (与えられた行列が変更されないことが明確に示されているため、後者の方が好みです)。

sqr_matrix_trace_const(a, n)

次の警告が生成されます。

sqr_matrix.c: In function 'main':
sqr_matrix.c:44: warning: passing argument 1 of 'sqr_matrix_trace_const' from incompatible pointer type
sqr_matrix.c:27: note: expected 'const double * const*' but argument is of type 'double **'

キャストはこれを克服します:

sqr_matrix_trace_const((const double * const *)a, n)

しかし、コンパイラの不便さを克服するためにキャストを使用するのは間違っていると感じます。

または、コンパイラの警告を抑制することもできますが、それは失敗です。

そのため、コードをきれいにコンパイルし、キャストに頼らずに関数に与えられた動的 2D 配列の const 性を伝えたいと考えています。正当な目的のようです。これは可能ですか?そうでない場合、これを行うための標準/受け入れられている方法は何ですか?

4

3 に答える 3

3

C の昇格規則では、 からへのconst昇格は許可されていません。6.5.16.1 1 (これは関数呼び出しと 6.5.2.2 2 の代入に適用されます) によると、ポインターの変換は、指定された型にのみ修飾子を追加できます。T **const T const *

これは、次のようなコードを防ぐためです (6.5.16.1 6 の例):

const char **cpp;
char *p;
const char c = 'A';
cpp = &p; // constraint violation
*cpp = &c; // valid
*p = 0; // valid

const *const *cpp = &pthen が防止されているので安全であると考えるのは正しい*cpp = &cですが、これは十分にあいまいなケースであり、標準でカバーされていません。

結論: 自分自身にキャストできますし、そうすべきconst double *const *です。

double *長さのある型の単一の配列を使用n * nし、必要な配列のインデックス付けを自分で行う方が効率的であることに注意してd[i][j]くださいd[i * n + j]

于 2012-06-13T12:35:07.087 に答える
1

C++ コンパイラはそれを可能にします。

C に関しては、修飾されたポインター型は再帰的に適用されません

于 2012-06-13T12:34:24.227 に答える
0

double *マトリックスデータが本当に2Dで長方形である場合(「ギザギザの右端」がない場合)、幅と高さを与える整数とともに、最初の要素まで単一として表していない理由がわかりません。これにより、マトリックスを初期化するために必要な割り当ての数を削減できるだけでなく、単純な古い として表現できるようになりconst double *ます。

于 2012-06-13T12:38:11.983 に答える