采用const 2d数组的C函数

时间:2012-06-13 11:52:26

标签: c arrays 2d const

我想编写一个C函数,它将动态2D数组作为输入,但不会改变数组。

我正在努力使const变得正确,不仅是为了让我的代码更清晰,而且因为我的函数将在C ++代码中被调用,而C ++对这些东西非常苛刻。

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

以下是一个特定的,超简单的例子。我正在使用二维的二维数组,即双**,来表示大小为n x n的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)

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

或者,我可以抑制编译器警告,但那是一个警告。

所以,我希望我的代码能够干净地编译,并且我想传达给定函数的动态2D数组的常量而不需要使用强制转换。这似乎是一个合法的目标。这可能吗?如果没有,这样做的标准/接受做法是什么?

3 个答案:

答案 0 :(得分:3)

C const促销规则不允许从T **升级到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是安全的,因为*cpp = &c被阻止了,但这是一个足够模糊的案例,标准中没有涉及。

结论:您可以而且应该自己投降const double *const *

请注意,使用长度为double *的{​​{1}}类型的单个数组会更高效,并自行执行任何必要的数组索引:n * n变为d[i][j]。< / p>

答案 1 :(得分:1)

C ++编译器允许这样做。

至于C,qualified pointer types are not applied recursively

答案 2 :(得分:0)

如果您的矩阵数据确实是2D和矩形(没有“粗糙的右边缘”),我不明白为什么你不将它作为单个double *表示为第一个元素,以及整数给出宽度和高度。这样可以减少初始化矩阵所需的分配数量,同时也可以将其表示为普通的const double *