数组指针的常量正确性?
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);
有人争论说在现代 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 typeconst 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);
进一步阅读:
除了施法没有办法。这是以这种方式传递数组的想法的重大缺点。
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",因为它类似于
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);