const数组指针的正确性?

时间:2016-03-24 11:49:17

标签: c const-correctness

有人提出一个论点,说在现代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正确性应用于作为参数传递的数组指针?它有可能吗?

修改

就像信息一样,有人说通过像这样的指针传递数组的想法可能源于MISRA C ++:2008 5-2-12。请参阅示例PRQA's high integrity C++ standard

3 个答案:

答案 0 :(得分:5)

除了演员之外,没有办法做到这一点。这是以这种方式传递数组的想法的重大缺点。

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

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

这种技术在传递非VLA数组时更有价值,当编译器必须报告是否将指针传递给错误长度的数组时。

答案 1 :(得分:4)

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

  

如果数组类型的规范包含任何类型限定符,则元素类型是合格的,不是数组类型。[...]

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

  

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

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

但是,GCC允许这作为extension

  

在GNU C中,带有限定符的数组的指针与指向其他限定类型的指针的工作方式类似。例如,类型int (*)[5]的值可用于初始化类型const int (*)[5]的变量。 这些类型在ISO C中是不兼容的,因为const限定符正式附加到数组的元素类型而不是数组本身

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

进一步阅读:Pointer to array with const qualifier in C & C++

答案 2 :(得分:2)

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

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

使用C11,根据需要使用_Generic进行投射。当输入为可接受的非const类型时,会发生强制转换 ,从而保持类型安全。这是"怎么"去做吧。 OP可能会考虑它"臃肿"因为它类似于this。此方法将宏/函数调用简化为仅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);