什么时候将数组转换为指针?
When are arrays converted to pointers?
考虑以下计算数组长度的简单示例:
#include <iostream>
int a[] = {1, 2, 4};
int main(){ std::cout << sizeof(a)/sizeof(a[0]) << std::endl; }
标准N4296::8.3.4/7 [dcl.array]
If E is an n-dimensional array of rank i×j×. . .×k, then E appearing
in an expression that is subject to the array-to-pointer conversion
(4.2) is converted to a pointer to an (n−1)-dimensional array with
rank j ×. . .×k.
N4296::4.2/1 [conv.array]
An lvalue or rvalue of type “array of N T” or “array of unknown bound
of T” can be converted to a prvalue of type “pointer to T”. The result
is a pointer to the first element of the array.
那么转换对象的表达式是什么?看起来未评估的操作数不是主题。
我知道以下表达式中的数组不是 converted/decayed 指针。
- 在
sizeof
运算符中使用时:sizeof(array)
- 在 addressof 运算符中使用时:
&array
- 当用于将引用绑定到数组时:
int (&ref)[3] = array;
.
- 推导用于实例化模板的类型名时。
- 在
decltype
中使用时:decltype(array)
在您的示例代码中,a[0]
与 *(a + 0)
相同,因此需要进行数组到指针的转换。请参阅 内置下标运算符 部分 here。
我工作的经验法则是 "in any part of an expression that produces a value result that can be stored in a pointer but cannot be stored in an array"。
所以,例如;
- 表达式
array + 0
在做加法之前将array
转换为一个指针,并给出一个指针的结果。
f(array)
在调用接受指针或数组(不是引用)的函数 f()
之前将 array
转换为指针。
array[0]
不需要将 array
转换为指针(但是
编译器可以自由选择,因为它对该表达式的结果没有影响)。
sizeof array
不会将 array
转换为指针(因为它不会
评估 array
,只是它的大小)
- 表达式
p = array
将 array
转换为指针和该值
存储在 p
我确定我错过了一些案例,但这个简单的规则相当有效。当然是建立在理解什么是表达式的基础上.....
我不知道是否有人可以随心所欲地列出所有规则,因此社区 wiki 可能是合适的。
数组到指针的转换发生在以下上下文中。所有参考均针对 C++11 标准。
- 作为重载决议选择的隐式转换序列的一部分1
- 作为标准转换序列的一部分,在允许的上下文中
- 从数组 ([dcl.init]/16) 初始化非 class 类型的对象时2
- 从数组 ([expr.ass]/3)
中分配给非 class 类型的左值时
- 当需要指针类型的纯右值作为内置运算符的操作数时 ([expr]/8)
- 下标入数组时([expr.sub]/1)
- 取消引用指针时 ([expr.unary.op]/1)
- 使用一元
+
运算符 ([expr.unary.op]/7)
- 使用二进制
+
运算符 ([expr.add]/1)
- 使用二进制
-
运算符 ([expr.add]/2)
- 与关系运算符 ([expr.rel]/1)
- 使用相等运算符 ([expr.eq]/1)
- 调用函数时,如果参数为数组类型并传递给省略号([expr.call]/7)
- 从指向基 class 的指针转换为指向派生 class 的指针时 ([expr.static.cast]/11)
- 在重新解释转换为非引用类型时 ([expr.reinterpret.cast]/1)
- 在转换为非引用类型的 const 中 ([expr.const.cast]/1)
- 在条件运算符的第二个或第三个操作数中,在某些情况下([expr.cond])
- 在模板参数中,如果相应的(非类型)模板参数具有指向对象类型的指针 ([temp.arg.nontype]/5)
在以下情况下不会发生数组到指针的转换:
- 需要左值(或左值)的地方
- 通过一元
&
运算符 ([expr.unary.op]/3)
- 静态转换为引用类型 ([expr.static.cast]/2, [expr.static.cast]/3)
- 在重新解释转换为引用类型 ([expr.reinterpret.cast]/11)
- 在 const 转换为引用类型 ([expr.const.cast]/4)
- 绑定到对相同数组类型的引用时
- 在丢弃值表达式中 ([expr]/10)
- 在
sizeof
的操作数中 ([expr.sizeof]/4)
- 当条件运算符的第二个和第三个操作数具有相同的数组类型并且都是相同值类别的左值时
- 在内置逗号运算符的任一操作数中
1 这包括将 T
的数组传递给期望 cv [=16= 的函数的情况]、cv void*
或 bool
,当用户定义的转换需要其中一种类型时,等
2 这包括在 if
语句等中出现的 bool
的上下文转换。
考虑以下计算数组长度的简单示例:
#include <iostream>
int a[] = {1, 2, 4};
int main(){ std::cout << sizeof(a)/sizeof(a[0]) << std::endl; }
标准N4296::8.3.4/7 [dcl.array]
If E is an n-dimensional array of rank i×j×. . .×k, then E appearing in an expression that is subject to the array-to-pointer conversion (4.2) is converted to a pointer to an (n−1)-dimensional array with rank j ×. . .×k.
N4296::4.2/1 [conv.array]
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.
那么转换对象的表达式是什么?看起来未评估的操作数不是主题。
我知道以下表达式中的数组不是 converted/decayed 指针。
- 在
sizeof
运算符中使用时:sizeof(array)
- 在 addressof 运算符中使用时:
&array
- 当用于将引用绑定到数组时:
int (&ref)[3] = array;
. - 推导用于实例化模板的类型名时。
- 在
decltype
中使用时:decltype(array)
在您的示例代码中,a[0]
与 *(a + 0)
相同,因此需要进行数组到指针的转换。请参阅 内置下标运算符 部分 here。
我工作的经验法则是 "in any part of an expression that produces a value result that can be stored in a pointer but cannot be stored in an array"。
所以,例如;
- 表达式
array + 0
在做加法之前将array
转换为一个指针,并给出一个指针的结果。 f(array)
在调用接受指针或数组(不是引用)的函数f()
之前将array
转换为指针。array[0]
不需要将array
转换为指针(但是 编译器可以自由选择,因为它对该表达式的结果没有影响)。sizeof array
不会将array
转换为指针(因为它不会 评估array
,只是它的大小)- 表达式
p = array
将array
转换为指针和该值 存储在p
我确定我错过了一些案例,但这个简单的规则相当有效。当然是建立在理解什么是表达式的基础上.....
我不知道是否有人可以随心所欲地列出所有规则,因此社区 wiki 可能是合适的。
数组到指针的转换发生在以下上下文中。所有参考均针对 C++11 标准。
- 作为重载决议选择的隐式转换序列的一部分1
- 作为标准转换序列的一部分,在允许的上下文中
- 从数组 ([dcl.init]/16) 初始化非 class 类型的对象时2
- 从数组 ([expr.ass]/3) 中分配给非 class 类型的左值时
- 当需要指针类型的纯右值作为内置运算符的操作数时 ([expr]/8)
- 下标入数组时([expr.sub]/1)
- 取消引用指针时 ([expr.unary.op]/1)
- 使用一元
+
运算符 ([expr.unary.op]/7) - 使用二进制
+
运算符 ([expr.add]/1) - 使用二进制
-
运算符 ([expr.add]/2) - 与关系运算符 ([expr.rel]/1)
- 使用相等运算符 ([expr.eq]/1)
- 调用函数时,如果参数为数组类型并传递给省略号([expr.call]/7)
- 从指向基 class 的指针转换为指向派生 class 的指针时 ([expr.static.cast]/11)
- 在重新解释转换为非引用类型时 ([expr.reinterpret.cast]/1)
- 在转换为非引用类型的 const 中 ([expr.const.cast]/1)
- 在条件运算符的第二个或第三个操作数中,在某些情况下([expr.cond])
- 在模板参数中,如果相应的(非类型)模板参数具有指向对象类型的指针 ([temp.arg.nontype]/5)
在以下情况下不会发生数组到指针的转换:
- 需要左值(或左值)的地方
- 通过一元
&
运算符 ([expr.unary.op]/3) - 静态转换为引用类型 ([expr.static.cast]/2, [expr.static.cast]/3)
- 在重新解释转换为引用类型 ([expr.reinterpret.cast]/11)
- 在 const 转换为引用类型 ([expr.const.cast]/4)
- 通过一元
- 绑定到对相同数组类型的引用时
- 在丢弃值表达式中 ([expr]/10)
- 在
sizeof
的操作数中 ([expr.sizeof]/4) - 当条件运算符的第二个和第三个操作数具有相同的数组类型并且都是相同值类别的左值时
- 在内置逗号运算符的任一操作数中
1 这包括将 T
的数组传递给期望 cv [=16= 的函数的情况]、cv void*
或 bool
,当用户定义的转换需要其中一种类型时,等
2 这包括在 if
语句等中出现的 bool
的上下文转换。