const数组指针的正确性?

有人提出一个论点,说在现代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])那么它是const正确的。 但是由于指针类型不兼容,我无法再传递数组。 int (*)[3]const int (*)[3] 。 限定符属于指向数据而不属于指针本身。

调用者中的显式强制转换会破坏增加类型安全性的整个想法。

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


编辑

就像信息一样,有人说通过像这样的指针传递数组的想法可能源于MISRA C ++:2008 5-2-12。 例如,请参阅PRQA的高完整性C ++标准 。

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

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

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

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

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

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

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

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

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

但是,GCC允许这作为扩展 :

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

进一步阅读: 在C&C ++中使用const限定符指向数组的指针

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可能认为它“臃肿”,因为它类似于此 。 此方法将宏/函数调用简化为仅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);