数组、指针、字符串字面量、以及数组到指针的隐式转换
数组名就是指向其首元素的指针。
你可能听说过,甚至在你的教材上看见过这样的说法。但这种说法十分不严谨,甚至可以说是大错特错。正式的说法是:“发生了数组到指针的隐式转换,数组隐式转换为指向其首元素的指针”。
隐式转换非常常见,甚至很多场景你都没有意识到发生了隐式转换。例如:
double d=1;
在这里,字面量1的类型是int,在初始化d的时候会发生隐式转换,从int转换到double。
隐式转换还有一点值得注意的是,隐式转换得到的值可能和原来的值不同。例如浮点转换到整数的时候会舍弃小数,大整数类型转换到小整数类型可能会发生截断。
隐式转换有很多细节,并且C和C++之间也有很多差异,建议大家参考cppreference中关于隐式转换的说明。而数组到指针转换是其中的一条规则,具体如下:
除了下列语境,任何数组类型的左值表达式都会转换为指向其首元素的指针右值:

1、以字符串字面量初始化字符数组:
这等价于:
注意末尾包含一个结束符。
这是专属于字符串字面量的特殊规则。所以用一个数组初始化另一个数组也是不行的:
并且这仅适用于初始化。数组不能作为赋值运算符的左操作数——它会隐式转换为一个指针右值,而右值是不能被赋值的。
此外,在C++中,被初始化的这个字符数组的长度不能小于字符串的长度加一,也就是必须能放得下字符串字面量末尾的结束符,而C语言允许丢弃结束符:
这里提到字符串字面量,是因为字符串字面量在C和C++中都是左值表达式,并且指代一个数组。例如"Hello world"在C语言中的类型是char[12]而C++中则是const char[12](注意const的区别,虽然C语言中没有const限定,但是试图修改它却是未定义行为。不要尝试"Hello world"[0] = 'B')。
2、用作取地址运算符&的操作数:
上面提到,字符串字面量是指代数组的左值表达式,因此可以对它取地址。&"Hello world"在C中会得到char(*)[12],C++中则是const char(*)[12]。注意这和隐式转换得到的指针类型是不同的,对于数组T array[N],取地址得到的是T(*)[N],而隐式转换得到的是T*。
3、用作sizeof运算符的操作数:
sizeof用在数组上会得到数组的总字节大小,例如上图sizeof(str)会得到12,而sizeof(char*)则根据系统不同可能是4(32位)或者8(64位)甚至别的值。
4、用作alignof/_Alignof运算符的操作数:
获取对齐要求可能比较少见,alignof用在数组上得到的是其元素类型的对齐,由于alignof只接受类型名作为实参,通常不会造成歧义。
5、绑定到对应类型的数组引用:
这是C++特有的语法,可以将引用绑定到数组,需要注意的是类型和长度必须匹配才能绑定,否则会报错。
6、用作decltype说明符的实参:
这是C++特有的语法,用于获取某个对象或表达式的类型与值类别。当表达式的类型为数组时,不会发生隐式转换。例如图中decltype(str) str2,str2的类型推导为char[12]。
也就是说,对于某个数组 T array[N],在除了上述语境中,array都会被转换为&array[0]。这也是开头那句话的来源。在程序中使用array这个名字时,array永远都指代一个数组,但是很多语境都不接受数组,于是发生了隐式转换,T[N]变成了T*。这个过程有时也称为数组到指针的退化,因为数组的长度信息在隐式转换的过程中丢失了。
QQ频道【std::forward编程社区】欢迎各位前来交流。
频道号:wxj6l1350o
入频链接:https://pd.qq.com/s/alapspris