当使用来自 operator new 的 return 指针作为指向数组元素的指针时,它是否是未定义的行为
Is it an undefined behavior when use the pointer that return from operator new as a pointer to element of array
int main(){
auto* ptr = (int*) ::operator new(sizeof(int)*10, std::align_val_t(alignof(int))); //#1
ptr[1] = 4; //#a
}
考虑上面的代码,标准所说的内容如下:
- The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type ([new.delete.single]) and then used to access the object or array in the storage allocated
- If the allocated type is a non-array type, the allocation function's name is operator new and the deallocation function's name is operator delete. If the allocated type is an array type, the allocation function's name is operator new[] and the deallocation function's name is operator delete[].
- If the entity is a non-array object, the new-expression returns a pointer to the object created. If it is an array, the new-expression returns a pointer to the initial element of the array
关于指针运算的规则说:
expr.add#4
- If the expression P points to element x[i] of an array object x with n elements,86 the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i+j] if 0≤ i+j ≤ n; otherwise, the behavior is undefined.
所以,我想知道在#a 处使用指针时是否存在未定义的行为?我认为它违反了项目符号 4。另外,查看 MSVC
的 std::allocate 的实现。似乎使用 operator new() 来分配 space 并使用 return 指针作为指向数组元素的指针。
好像标准没有说直接调用::operator new(...)时return指针指向什么原始对象。它只说调用此类分配函数产生的 return 指针可以转换为指向已适当对齐的对象的指针。
更新:
我关心的是dynamic-construction-of-arrays
大多数std::vector的实现使用std::allocate并且std::vector有一个非静态数据成员记录来自std::allocate的结果。当使用std::vector的对象作为arr[i]
时,实现将使用非静态数据成员作为指向数组类型元素的指针来访问arr[i]
。我觉得应该是UB?即,我们被允许使用分配函数中 return 的指针作为 new-placement
的操作数来构造一个对象,但是如果我们使用指针访问 ith
对象或任何迭代器来access ith
object, 表示是UB?
表达式:
::operator new(sizeof(int)*10, std::align_val_t(alignof(int)));
是全局分配函数的函数调用表达式。它不使用 new
表达式来分配存储和构造对象或对象数组。全局分配器仅 return 原始存储,不在分配的内存中构造对象。
里面basic.stc.dynamic.allocation
- The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type ([new.delete.single]) and then used to access the object or array in the storage allocated [...]
对象是一个未隐式创建的对象。应该是根据代码中的[intro.object]/1创建的
因此,在这种情况下,您知道表达式 ptr[1]
具有概念上 2 未定义的行为:
ptr+1
是未定义的行为,因为 ptr
value is not a pointer to array expr.add
*(ptr+1)
是未定义的行为,因为 ptr
的值不是 指向对象的指针 [expr.unary.op]/1
根据 c++20,此代码具有明确定义的行为。因为隐式创建的 int[N]
类型的数组对象 N>1
及其元素也被隐式创建会给出此代码定义的行为。
Any implicit or explicit invocation of a function named operator new or operator new[] implicitly creates objects in the returned region of storage and returns a pointer to a suitable created object.
Some operations are described as implicitly creating objects within a specified region of storage. For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types]) in its specified region of storage if doing so would result in the program having defined behavior. If no such set of objects would give the program defined behavior, the behavior of the program is undefined. If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created.
这两段是语言指定方式的一种革命:
- 未确定会发生什么,而是一个可能的集合隐式创建并启动零个或多个隐式生命周期类型对象的生命周期
- 约束不依赖于代码或程序执行中的给定点,而是依赖于整个程序执行:如果这样做会导致程序具有定义的行为.
所以代码有效性取决于归纳,这就是我认为的革命。例如在 abose 的情况下,推理是:让我们假设分配函数调用 returned 一个指向类型 int [1]
的对象的指针,所以定义的代码行为,所以假设是正确的
但是在整个程序执行之前,这个隐式对象仍然是假设的。例如,如果代码中的其他地方在 ptr[2]
处创建了 int
,则推理可以更改为:让我们假设分配函数调用将 returned a指向int [2]
类型对象的指针,所以代码定义为行为,所以假设是正确的
int main(){
auto* ptr = (int*) ::operator new(sizeof(int)*10, std::align_val_t(alignof(int))); //#1
ptr[1] = 4; //#a
}
考虑上面的代码,标准所说的内容如下:
- The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type ([new.delete.single]) and then used to access the object or array in the storage allocated
- If the allocated type is a non-array type, the allocation function's name is operator new and the deallocation function's name is operator delete. If the allocated type is an array type, the allocation function's name is operator new[] and the deallocation function's name is operator delete[].
- If the entity is a non-array object, the new-expression returns a pointer to the object created. If it is an array, the new-expression returns a pointer to the initial element of the array
关于指针运算的规则说:
expr.add#4
- If the expression P points to element x[i] of an array object x with n elements,86 the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i+j] if 0≤ i+j ≤ n; otherwise, the behavior is undefined.
所以,我想知道在#a 处使用指针时是否存在未定义的行为?我认为它违反了项目符号 4。另外,查看 MSVC
的 std::allocate 的实现。似乎使用 operator new() 来分配 space 并使用 return 指针作为指向数组元素的指针。
好像标准没有说直接调用::operator new(...)时return指针指向什么原始对象。它只说调用此类分配函数产生的 return 指针可以转换为指向已适当对齐的对象的指针。
更新:
我关心的是dynamic-construction-of-arrays
大多数std::vector的实现使用std::allocate并且std::vector有一个非静态数据成员记录来自std::allocate的结果。当使用std::vector的对象作为arr[i]
时,实现将使用非静态数据成员作为指向数组类型元素的指针来访问arr[i]
。我觉得应该是UB?即,我们被允许使用分配函数中 return 的指针作为 new-placement
的操作数来构造一个对象,但是如果我们使用指针访问 ith
对象或任何迭代器来access ith
object, 表示是UB?
表达式:
::operator new(sizeof(int)*10, std::align_val_t(alignof(int)));
是全局分配函数的函数调用表达式。它不使用 new
表达式来分配存储和构造对象或对象数组。全局分配器仅 return 原始存储,不在分配的内存中构造对象。
里面basic.stc.dynamic.allocation
- The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type ([new.delete.single]) and then used to access the object or array in the storage allocated [...]
对象是一个未隐式创建的对象。应该是根据代码中的[intro.object]/1创建的
因此,在这种情况下,您知道表达式 ptr[1]
具有概念上 2 未定义的行为:
ptr+1
是未定义的行为,因为ptr
value is not a pointer to array expr.add*(ptr+1)
是未定义的行为,因为ptr
的值不是 指向对象的指针 [expr.unary.op]/1
根据 c++20,此代码具有明确定义的行为。因为隐式创建的 int[N]
类型的数组对象 N>1
及其元素也被隐式创建会给出此代码定义的行为。
Any implicit or explicit invocation of a function named operator new or operator new[] implicitly creates objects in the returned region of storage and returns a pointer to a suitable created object.
Some operations are described as implicitly creating objects within a specified region of storage. For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types]) in its specified region of storage if doing so would result in the program having defined behavior. If no such set of objects would give the program defined behavior, the behavior of the program is undefined. If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created.
这两段是语言指定方式的一种革命:
- 未确定会发生什么,而是一个可能的集合隐式创建并启动零个或多个隐式生命周期类型对象的生命周期
- 约束不依赖于代码或程序执行中的给定点,而是依赖于整个程序执行:如果这样做会导致程序具有定义的行为.
所以代码有效性取决于归纳,这就是我认为的革命。例如在 abose 的情况下,推理是:让我们假设分配函数调用 returned 一个指向类型 int [1]
的对象的指针,所以定义的代码行为,所以假设是正确的
但是在整个程序执行之前,这个隐式对象仍然是假设的。例如,如果代码中的其他地方在 ptr[2]
处创建了 int
,则推理可以更改为:让我们假设分配函数调用将 returned a指向int [2]
类型对象的指针,所以代码定义为行为,所以假设是正确的