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 Aconst 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:对于fpchar * 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]) {
     ....
}