Tom*_*Tom 22 c++ casting type-punning
我很好奇C++中类型惩罚指针/数组的约定.这是我目前的用例:
通过将其视为32位整数数组(我们知道它的总长度是4的倍数),然后将所有值相加并忽略溢出,计算二进制blob数据的简单32位校验和.
我希望这样的函数看起来像这样:
uint32_t compute_checksum(const char *data, size_t size)
{
const uint32_t *udata = /* ??? */;
uint32_t checksum = 0;
for (size_t i = 0; i != size / 4; ++i)
checksum += udata[i];
return udata;
}
Run Code Online (Sandbox Code Playgroud)
现在我的问题是,您认为转换data为"最佳"的方式是udata什么?
C风格演员?
udata = (const uint32_t *)data
Run Code Online (Sandbox Code Playgroud)
假设所有指针都是可转换的C++强制转换?
udata = reinterpret_cast<const uint32_t *>(data)
Run Code Online (Sandbox Code Playgroud)
C++在任意指针类型之间使用中间转换void*?
udata = static_cast<const uint32_t *>(static_cast<const void *>(data))
Run Code Online (Sandbox Code Playgroud)
通过工会铸造?
union {
const uint32_t *udata;
const char *cdata;
};
cdata = data;
// now use udata
Run Code Online (Sandbox Code Playgroud)
我完全意识到这不是一个100%可移植的解决方案,但我只希望在一小部分平台上使用它,我知道它可以工作(即未对齐的内存访问和指针别名的编译器假设).你会推荐什么?
Ada*_*eld 13
就C++标准而言,litb的答案是完全正确的,也是最便携的.无论是通过C风格的强制转换,还是强制转换const char *data为a const uint3_t *,都会违反严格的别名规则(请参阅了解严格别名).如果您使用完全优化进行编译,那么代码很可能不会正确.static_castreinterpret_cast
通过联合(例如litb my_reint)进行转换可能是最好的解决方案,尽管它在技术上违反了规则,如果您通过一个成员写入联合并通过另一个成员读取它,则会导致未定义的行为.但是,几乎所有编译器都支持这一点,并且它会产生预期的结果.如果您绝对希望符合标准100%,请使用位移方法.否则,我建议通过联盟进行投射,这可能会给你更好的表现.
忽略效率,为了简化代码我会做:
#include <numeric>
#include <vector>
#include <cstring>
uint32_t compute_checksum(const char *data, size_t size) {
std::vector<uint32_t> intdata(size/sizeof(uint32_t));
std::memcpy(&intdata[0], data, size);
return std::accumulate(intdata.begin(), intdata.end(), 0);
}
Run Code Online (Sandbox Code Playgroud)
我也喜欢litb的最后一个答案,即依次移动每个char的那个,除了因为char可能会被签名,我认为它需要一个额外的掩码:
checksum += ((data[i] && 0xFF) << shift[i % 4]);
Run Code Online (Sandbox Code Playgroud)
当打字类型是一个潜在的问题时,我宁愿不输入双关语而不是安全地尝试这样做.如果您不首先创建任何不同类型的别名指针,那么您不必担心编译器可能对别名做什么,维护程序员也不会通过联合看到您的多个static_cast.
如果你不想分配这么多额外的内存,那么:
uint32_t compute_checksum(const char *data, size_t size) {
uint32_t total = 0;
for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
uint32_t thisone;
std::memcpy(&thisone, &data[i], sizeof(uint32_t));
total += thisone;
}
return total;
}
Run Code Online (Sandbox Code Playgroud)
足够的优化将完全取消内存中的memcpy和额外的uint32_t变量,只需读取一个未对齐的整数值,无论以何种最有效的方式在您的平台上执行,直接从源数组中取出.我希望其他"严肃"的编译器也是如此.但是这个代码现在比litb更大了,所以没有什么可说的,除了我之外更容易变成一个与uint64_t一样好用的函数模板,并且我的工作作为本地字节序而不是选择一点-endian.
这当然不是完全便携的.它假定sizeof(uint32_t)字符的存储表示以我们想要的方式对应于uin32_t的存储表示.问题暗示了这一点,因为它表明一个人可以被"视为"另一个人.Endian-ness,char是否是8位,以及uint32_t是否使用其存储表示中的所有位显然可以侵入,但问题暗示它们不会.
| 归档时间: |
|
| 查看次数: |
8572 次 |
| 最近记录: |