数组指针的常量正确性?

Const correctness for array pointers?

有人争论说在现代 C 中,我们应该始终通过数组指针将数组传递给函数,因为数组指针具有强类型。示例:

void func (size_t n, int (*arr)[n]);
...

int array [3];
func(3, &array);

这听起来可能是防止各种类型相关和数组越界错误的好主意。但后来我突然想到,我不知道如何对此应用 const 正确性。

如果我这样做 void func (size_t n, const int (*arr)[n]) 那么它是正确的。但是由于指针类型不兼容,我无法再传递数组。 int (*)[3] 对比 const int (*)[3]。限定符属于指向的数据而不属于指针本身。

在调用者中进行显式强制转换会破坏提高类型安全性的整个想法。

如何将 const 正确性应用于作为参数传递的数组指针?有可能吗?


编辑

正如info,有人说像这样通过指针传递数组的想法可能起源于MISRA C++:2008 5-2-12。参见示例 PRQA's high integrity C++ standard

C 标准说(部分:§6.7.3/9):

If the specification of an array type includes any type qualifiers, the element type is so- qualified, not the array type.[...]

因此,在 const int (*arr)[n] 的情况下,const 应用于数组的元素而不是数组 arr 本身。 arr 的类型是 指向 const int 的数组 [n] 的指针,而您传递的参数类型是 指向数组 [n] 的 int[=] 35=]。这两种类型是不兼容的。

How do I apply const correctness to array pointers passed as parameters? Is it at all possible?

这不可能。如果不使用显式强制转换,则无法在标准 C 中执行此操作。

但是,GCC 允许将此作为​​ extension

In GNU C, pointers to arrays with qualifiers work similar to pointers to other qualified types. For example, a value of type int (*)[5] can be used to initialize a variable of type const int (*)[5]. These types are incompatible in ISO C because the const qualifier is formally attached to the element type of the array and not the array itself.

 extern void
 transpose (int N, int M, double out[M][N], const double in[N][M]);
 double x[3][2];
 double y[2][3];
 ...
 transpose(3, 2, y, x); 

进一步阅读:

除了施法没有办法。这是以这种方式传递数组的想法的重大缺点。

一个类似的线程,其中将 C 规则与 C++ 规则进行比较。我们可以从这个比较中得出结论,C 规则设计得不是很好,因为您的用例是有效的,但 C 不允许隐式转换。另一个这样的例子是 T **T const * const * 的转换;这是安全的,但 C 不允许。

请注意,由于 n 不是常量表达式,因此与 int n, int *arr 相比,int n, int (*arr)[n] 没有任何附加的类型安全性。您仍然知道长度 (n),越界访问仍然是无声未定义行为,传递实际上不是长度 n 的数组仍然是无声未定义行为。

此技术在传递非 VLA 数组的情况下更有价值,如果您传递指向错误长度数组的指针,编译器必须报告。

OP 描述了具有以下签名的函数 func()

void func(size_t n, const int (*arr)[n])

OP 想调用它传递各种数组

#define SZ(a) (sizeof(a)/sizeof(a[0]))

int array1[3];
func(SZ(array1), &array1);  // problem

const int array2[3] = {1, 2, 3};
func(SZ(array2), &array2);

How do I apply const correctness to array pointers passed as parameters?

对于 C11,使用 _Generic 根据需要进行转换。当输入是可接受的非 const 类型时,转换 only 发生,从而保持类型安全。这是"how"要做的。 OP 可能会认为它是 "bloated",因为它类似于 。这种方法将 macro/function 调用简化为仅 1 个参数。

void func(size_t n, const int (*arr)[n]) {
  printf("sz:%zu (*arr)[0]:%d\n", n, (*arr)[0]);
}

#define funcCC(x) func(sizeof(*x)/sizeof((*x)[0]), \
  _Generic(x, \
  const int(*)[sizeof(*x)/sizeof((*x)[0])] : x, \
        int(*)[sizeof(*x)/sizeof((*x)[0])] : (const int(*)[sizeof(*x)/sizeof((*x)[0])])x \
  ))

int main(void) {
  #define SZ(a) (sizeof(a)/sizeof(a[0]))

  int array1[3];
  array1[0] = 42;
  // func(SZ(array1), &array1);

  const int array2[4] = {1, 2, 3, 4};
  func(SZ(array2), &array2);

  // Notice only 1 parameter to the macro/function call
  funcCC(&array1);  
  funcCC(&array2);

  return 0;
}

输出

sz:4 (*arr)[0]:1
sz:3 (*arr)[0]:42
sz:4 (*arr)[0]:1

或者代码可以使用

#define funcCC2(x) func(sizeof(x)/sizeof((x)[0]), \
    _Generic(&x, \
    const int(*)[sizeof(x)/sizeof((x)[0])] : &x, \
          int(*)[sizeof(x)/sizeof((x)[0])] : (const int(*)[sizeof(x)/sizeof((x)[0])])&x \
    ))

funcCC2(array1);
funcCC2(array2);