Const 与数组到指针的转换
Const vs. array-to-pointer conversions
fa
中对 constness 的处理是有意的,还是看起来像一个错误(编译器或标准)?我知道这是怎么发生的,但仍然感觉很奇怪(至少我希望 a = "qwe"
也被视为错误——就像 fs
中一样)。
typedef char A[8];
typedef char *P;
typedef std::string S;
void fa( const A a ) {
a = "qwe"; // ok
// *a = 'Q'; // error
}
void fp( const P p ) {
// p = "qwe"; // error
*p = 'Q'; // ok
}
void fs( const S s ) {
// s = "qwe"; // error
// *s = 'Q'; // error
}
[MinGW 4.9.1]
澄清:我理解这种转换的一般方式,因此是 "I can see how this can happen" 部分。这个问题是关于 (a) std 引用(感谢那些提供它们的人),以及 (b) 这个怪癖是否有实际的理由(这是非常违反直觉的)。
A
的类型为 char[8]
。 const A
是 const char[8]
。 fa
的参数因此是 const char[8]
,它衰减为指针 const char*
,所以在 fa
中一切都很好,因为您正在分配一个字符串文字(类型 const char[SIZE]
) 指向指针 a
).
P
的类型为 char*
。 const P
具有类型 char* const
(不是 const char*
),因为常量应用于指针。所以你不能将 p
分配给其他指针。 *p = 'Q'
可以,因为您正在分配指针指向的位置。因此,第二个函数 fb
的行为也符合预期。
这是正确的:fa
的参数类型是const char *
,不是char * const
。这是数组声明含义的一个微妙之处,以及将数组类型调整为指针类型作为函数参数。
首先调整数组类型本身:
C++11 8.3.4/1: Any type of the form “cv-qualifier-seq array of N T” is adjusted to “array of N cv-qualifier-seq T”
所以最初,类型变为 "array of const char
"。然后,由于是函数参数,所以进一步调整类型:
C++11 8.3.5/5: any parameter of type “array of T” [...] is
adjusted to be “pointer to T”
所以最后的类型是"pointer to const char
",或者const char *
。因此,如您的示例所示,您可以修改指针,但不能修改它指向的数据。
对于其他例子,参数类型本身就是const
:对于fp
是char * const
,允许修改数据但不能修改指针;对于 fp
,它是 const string
,根本不允许修改。
这个函数声明
void fa( const A a );
相当于
void fa( const char a[8] );
反过来相当于
void fa( const char *a );
并且它们都声明了同一个函数在函数中,参数的类型与上一个函数声明中的一样const char *
。
另一方面,字符串文字具有常量字符数组类型,在表达式中也隐式转换为指向其第一个元素的指针。
因此在这次作业中
a = "qwe"; // ok
具有类型 const char[4]
的字符串文字 "qwe" 隐式转换为类型 const char *
.
因此赋值左侧的类型为 const char *
,赋值右侧的类型相同 const char *
。
所以没有问题。
在此声明中考虑到这一点
const A a
typedef 名称被替换为原始类型,您得到
const char[8]
考虑到根据 C++ 标准(3.9.3 CV 限定符)
2 ... Any cv-qualifiers applied to an array type affect the array
element type, not the array type (8.3.4).
另一方面,在此声明中
const P p
P 被替换为 char *
并且你得到 char * const
那就是你得到可能不会改变其值的常量指针。
参数列表中的数组很奇怪。
void foo(char a[10], char b) {
char c[10];
char d;
}
如您所料,sizeof(d)
和 sizeof(b)
都是 1
。并且 sizeof(c)==10
,正如预期的那样。
但 sizeof(a)
将是 4 或 8,具体取决于您的平台的位数。它将是指针的大小。
所以基本上,参数列表中的数组不同于其他任何地方的数组。
所以c
确实是一个数组,你不能做c = "hi";
。
但编译器会默默地用指针替换参数中的数组。所以 void foo(char a[10], char b)
被改写为 void foo(char *a, char b)
.
并且 const
在数组被重写为指针之前应用于数组 - 所以你得到一个指向常量字符的非常量指针。
这里的教训是,参数里面的数组基本没用。所有大小信息都丢失了。编译器不会阻止您将大数组传递给小数组参数。
在参数列表中使用数组的唯一有用方法是作为参考。
void foo(char (&a)[10], b) {
}
在这种情况下,如果您传递的数组没有正好包含 10 个元素,您将收到一条错误消息。如果您分配给数组或数组的元素,您将获得预期的行为。
如果您需要知道数组的大小,您可以使用模板 'remember'。
template<size_t N>
void foo(char (&a)[N]) {
....
}
fa
中对 constness 的处理是有意的,还是看起来像一个错误(编译器或标准)?我知道这是怎么发生的,但仍然感觉很奇怪(至少我希望 a = "qwe"
也被视为错误——就像 fs
中一样)。
typedef char A[8];
typedef char *P;
typedef std::string S;
void fa( const A a ) {
a = "qwe"; // ok
// *a = 'Q'; // error
}
void fp( const P p ) {
// p = "qwe"; // error
*p = 'Q'; // ok
}
void fs( const S s ) {
// s = "qwe"; // error
// *s = 'Q'; // error
}
[MinGW 4.9.1]
澄清:我理解这种转换的一般方式,因此是 "I can see how this can happen" 部分。这个问题是关于 (a) std 引用(感谢那些提供它们的人),以及 (b) 这个怪癖是否有实际的理由(这是非常违反直觉的)。
A
的类型为 char[8]
。 const A
是 const char[8]
。 fa
的参数因此是 const char[8]
,它衰减为指针 const char*
,所以在 fa
中一切都很好,因为您正在分配一个字符串文字(类型 const char[SIZE]
) 指向指针 a
).
P
的类型为 char*
。 const P
具有类型 char* const
(不是 const char*
),因为常量应用于指针。所以你不能将 p
分配给其他指针。 *p = 'Q'
可以,因为您正在分配指针指向的位置。因此,第二个函数 fb
的行为也符合预期。
这是正确的:fa
的参数类型是const char *
,不是char * const
。这是数组声明含义的一个微妙之处,以及将数组类型调整为指针类型作为函数参数。
首先调整数组类型本身:
C++11 8.3.4/1: Any type of the form “cv-qualifier-seq array of N T” is adjusted to “array of N cv-qualifier-seq T”
所以最初,类型变为 "array of const char
"。然后,由于是函数参数,所以进一步调整类型:
C++11 8.3.5/5: any parameter of type “array of T” [...] is adjusted to be “pointer to T”
所以最后的类型是"pointer to const char
",或者const char *
。因此,如您的示例所示,您可以修改指针,但不能修改它指向的数据。
对于其他例子,参数类型本身就是const
:对于fp
是char * const
,允许修改数据但不能修改指针;对于 fp
,它是 const string
,根本不允许修改。
这个函数声明
void fa( const A a );
相当于
void fa( const char a[8] );
反过来相当于
void fa( const char *a );
并且它们都声明了同一个函数在函数中,参数的类型与上一个函数声明中的一样const char *
。
另一方面,字符串文字具有常量字符数组类型,在表达式中也隐式转换为指向其第一个元素的指针。
因此在这次作业中
a = "qwe"; // ok
具有类型 const char[4]
的字符串文字 "qwe" 隐式转换为类型 const char *
.
因此赋值左侧的类型为 const char *
,赋值右侧的类型相同 const char *
。
所以没有问题。
在此声明中考虑到这一点
const A a
typedef 名称被替换为原始类型,您得到
const char[8]
考虑到根据 C++ 标准(3.9.3 CV 限定符)
2 ... Any cv-qualifiers applied to an array type affect the array element type, not the array type (8.3.4).
另一方面,在此声明中
const P p
P 被替换为 char *
并且你得到 char * const
那就是你得到可能不会改变其值的常量指针。
参数列表中的数组很奇怪。
void foo(char a[10], char b) {
char c[10];
char d;
}
如您所料,sizeof(d)
和 sizeof(b)
都是 1
。并且 sizeof(c)==10
,正如预期的那样。
但 sizeof(a)
将是 4 或 8,具体取决于您的平台的位数。它将是指针的大小。
所以基本上,参数列表中的数组不同于其他任何地方的数组。
所以c
确实是一个数组,你不能做c = "hi";
。
但编译器会默默地用指针替换参数中的数组。所以 void foo(char a[10], char b)
被改写为 void foo(char *a, char b)
.
并且 const
在数组被重写为指针之前应用于数组 - 所以你得到一个指向常量字符的非常量指针。
这里的教训是,参数里面的数组基本没用。所有大小信息都丢失了。编译器不会阻止您将大数组传递给小数组参数。
在参数列表中使用数组的唯一有用方法是作为参考。
void foo(char (&a)[10], b) {
}
在这种情况下,如果您传递的数组没有正好包含 10 个元素,您将收到一条错误消息。如果您分配给数组或数组的元素,您将获得预期的行为。
如果您需要知道数组的大小,您可以使用模板 'remember'。
template<size_t N>
void foo(char (&a)[N]) {
....
}