std::memcpy或显式字符值赋值-在C++中等于合法
std::memcpy or explicit char value assignment - equal and legal in C++
在以下示例中,uint32_t
的值表示被复制到uint8_t
数组。这是由std::memcpy
完成的。根据我对C++标准的理解,这是完全合法的:我们正在通过向unsigned char*
广播的T*
访问T
类型的对象。没有混叠问题,没有对齐问题。
反之则不那么明显。我们正在通过unsigned char*
访问T
的对象表示,这是合法的。但是,访问一词是否包括更改?
当然,没有混叠和对齐问题。然而,如果缓冲区s
中的值来自外部源,则会出现问题:我们必须确保正确的端序并省略陷阱表示。可以检查右端序,这样就可以解决这个问题。但是陷阱表示呢?我们如何才能避免这种情况?或者uint
类型没有陷阱表示,而不是说double
?
我知道另一种(更符合要求的?)方式是将uint8_t
值转换为uint_t
对象。我们仍然必须遵守endianness,但这应该可以安全地省略陷阱表示。
但是,在小µC(8位)上进行大类型的移位可能相当昂贵!
下一个问题是,第二次尝试(见下文代码)在合法性和功能性方面是否等同于memcpy
方法?看起来memcpy
版本对优化器更友好。
#include <cstdint>
#include <cstring>
#include <cassert>
typedef uint32_t utype;
constexpr utype value = 0x01020304;
int main() {
utype a{value};
utype b{0};
uint8_t s[sizeof(utype)]{};
// first
std::memcpy(s, &a, sizeof(utype));
assert(s[0] == (value & 0xff));
std::memcpy(&b, s, sizeof(utype));
assert(b == value);
// second
const uint8_t* ap = reinterpret_cast<const uint8_t*>(&a);
s[0] = ap[0]; // explicitly legal in C++
s[1] = ap[1];
s[2] = ap[2];
s[3] = ap[3];
assert(s[0] == (value & 0xff));
uint8_t* bp = reinterpret_cast<uint8_t*>(&b);
bp[0] = s[0]; // same as memcpy or ist this UB ?
bp[1] = s[1];
bp[2] = s[2];
bp[3] = s[3];
assert(b == value);
}
但是术语访问是否包括更改?
是。
注:事实上,memcpy在概念上就是这么做的。它将字节修改为窄字符对象。如果这不可能,那么memcpy就无法在标准c++中实现。
但是陷阱表示呢?我们如何才能避免这种情况?
这很棘手。如果您知道陷阱表示,那么在尝试使用具有陷阱表示的类型中的值之前,您必须使用对象的窄字符视图来测试它。我不知道是否有任何标准的方法来处理陷阱表示。
也许应该有一个std::is_trap<T>(void*)
特性来解决这个问题,但据我所知,还没有。
我知道另一种(更符合要求的?)方法是将uint8_t值转移到uint_t对象中。我们仍然必须遵守endianness,但这应该可以安全地省略陷阱表示。
如果外部值是陷阱表示,那么该值可能无论如何都不可表示,因此这种移位可能会出现其他问题,例如在这种情况下溢出。
shift和memcpy之间的区别在于,shift允许将已知的endianness转换为本机endianness,而memcpy在源已经具有本机endianness时工作。
如果保证uint8_t
是unsigned char
的别名,那么第二个片段将被很好地定义,并且在功能上等同于memcpy。我不知道它是否有保证,但它肯定很常见。只有窄字符类型具有指针别名规则的例外。
assert(s[0] == (value & 0xff));
此断言依赖于cpu的端序。
- 为"adjacent"变量赋值时出现问题
- C++中的赋值发生,尽管右侧出现异常
- 用C++中的sscanf赋值
- 为std::string的某个索引赋值
- 重载Singly Linked List中的赋值运算符
- 为什么我必须在C++中添加一个赋值符号来声明一个数组
- gtest_使用setargpointee在函数中赋值
- 非常量变量只读位置的赋值
- 使用赋值运算符重载从类中返回jobject
- C++数据文件、数组和计算赋值
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- 全局作用域中函数指针的赋值
- 错误:在为指针赋值时,void值没有被忽略
- 标准库类型的赋值运算符的引用限定符
- 关于 c++ 函数中指针赋值的简单问题
- 复制构造函数、赋值运算符C++
- 标准::变体的赋值运算符
- cin >> int 给定一个字符串将 int 赋值为 0
- if 子句中的赋值不起作用
- 复制包含C++所有元素的对象!(构造函数和赋值,最佳实践?