std::memcpy(arr_copy, arr, sizeof arr);
(你的例子)是明确定义的。
std::memcpy(arr_copy, arr[0], sizeof arr);
另一方面,会导致未定义的行为(至少在 C++ 中;对于 C 不完全确定)。
多维数组是数组的一维数组。据我所知,与真正的一维数组(即具有非数组类型元素的数组)相比,它们没有得到太多(如果有的话)特殊待遇。
考虑一个一维数组的示例:
int a[3] = {1,2,3}, b[3];
std::memcpy(b, a, sizeof(int) * 3);
This is obviously legal.1
请注意memcpy
接收指向数组第一个元素的指针,并且可以访问其他元素。
元素类型不影响此示例的有效性。如果使用二维数组,元素类型变为int[N]
而不是int
,但有效性不受影响。
现在,考虑一个不同的例子:
int a[2][2] = {{1,2},{3,4}}, b[4];
std::memcpy(b, a[0], sizeof(int) * 4);
// ^~~~
This one causes UB2, because since memcpy
is given a pointer to the first element of a[0]
, it can only access the elements of a[0]
(a[0][i]
), and not a[j][i]
.
但是,如果你想听我的意见,这是一种“驯服”的 UB,在实践中可能不会造成问题(但是,一如既往,如果可能的话,应该避免 UB)。
1 The C++ standard doesn't explain memcpy
, and instead refers to the C standard. The C standard uses somewhat sloppy wording:
C11 (N1570)[7.24.2.1]/2 http://port70.net/%7Ensz/c/c11/n1570.html#7.24.2.1p2
The memcpy
函数副本n
指向的对象中的字符s2
进入指向的对象s1
.
指向数组的第一个(或任何)元素的指针仅指向该元素,而不指向整个数组,即使可以通过所述指针访问整个数组。因此,如果按字面解释,@LanguageLawyer 似乎是正确的:如果你给出memcpy
指向数组元素的指针,您只能复制该单个元素,而不能复制连续的元素。
这种解释与常识相矛盾,而且很可能不是有意的。
例如。考虑中的例子[basic.types.general]/2 http://eel.is/c++draft/basic#types.general-example-1,这适用于memcpy
使用指向第一个元素的指针到数组:(即使示例是非规范的)
constexpr std::size_t N = sizeof(T);
char buf[N];
T obj;
std::memcpy(buf, &obj, N);
std::memcpy(&obj, buf, N);
2 This is moot, because of the problematic wording for memcpy
described above.
我对 C 不太确定,但对于 C++,有强烈的暗示这是 UB。
首先,考虑一个使用的类似示例std::copy_n
,尝试执行按元素复制而不是按字节复制:
#include <algorithm>
consteval void foo()
{
int a[2][2] = {{1,2},{3,4}}, b[2][2] = {{1,2},{3,4}};
std::copy_n(a[0], 4, b[0]);
}
int main() {foo();}
在编译时运行函数会捕获大多数形式的 UB(它使代码格式错误),并且编译此代码片段确实会给出:
error: call to consteval function 'foo' is not a constant expression
note: cannot refer to element 4 of array of 2 elements in a constant expression
情况与memcpy
不太确定,因为它执行按字节复制。整个主题似乎是模糊且不明确 https://stackoverflow.com/q/62329008/2752075.
考虑以下措辞:std::launder
:
[ptr.launder]/4 http://eel.is/c++draft/ptr.launder#4
一个字节的存储空间b
可以通过指向对象的指针值访问Y
如果有一个物体Z
, 指针可与Y
,使得b
位于占用的存储空间内Z
,或直接封闭的数组对象,如果Z
是一个数组元素。
换句话说,给定一个指向数组元素的指针,该数组的所有元素都可以通过该指针访问(非递归地,即通过&a[0][0]
only a[0][i]
是可达的)。
形式上,这个定义仅用于描述std::launder
(事实上,它无法扩展给定的指针的可到达区域)。但其含义似乎是该定义总结了标准其他部分描述的可达性规则([static.cast]/13 http://eel.is/c++draft/expr#static.cast-13,请注意reinterpret_cast
is 定义通过 http://eel.is/c++draft/expr.reinterpret.cast#7相同的措辞;还[basic.compound]/4 http://eel.is/c++draft/basic.compound#4).
目前尚不完全清楚上述规则是否适用于memcpy
,但他们应该。因为否则,程序员将能够使用库函数忽略可达性,这将使可达性的概念几乎毫无用处。