4

我想编写一个将动态二维数组作为输入但不改变数组的 C 函数。

我试图使 const 正确,不仅是为了使我的代码更清晰,而且因为我的函数将从 C++ 代码中调用,而 C++ 对这些事情非常挑剔。

如何声明一个函数以将“const”指针指向一个指针,即如何指示该函数不会改变二维数组的内容?

下面是一个具体的、超级简单的例子。我正在使用一个二维的双精度数组,即 double** 来表示一个大小为 nxn 的 C 方阵,我想编写一个函数来计算这些矩阵之一的轨迹:

#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)

但是使用强制转换来克服编译器的不便感觉是错误的。

或者,我可以禁止编译器警告,但这是一种逃避。

因此,我希望我的代码能够干净地编译,并且我想传达给函数的动态二维数组的 const 特性,而无需借助强制转换。这似乎是一个合法的目标。这可能吗?如果没有,这样做的标准/公认做法是什么?

4

3 回答 3

3

Cconst升级规则不允许从T **to升级const T const *。根据 6.5.16.1 1(适用于函数调用以及根据 6.5.2.2 2 的赋值),指针的转换只能将限定符添加到指向的类型。

这是为了防止类似代码(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 = &p因为 then*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 *第一个元素的单个元素,以及给出宽度和高度的整数。这将使您既可以减少初始化矩阵所需的分配数量,又可以使其可以表示为普通的 old const double *

于 2012-06-13T12:38:11.983 回答