具有以下功能:
int encode(uint8b *dest, MyType srcType, const void *src)
{
uint32b value = 0;
uint64b value64 = 0;
switch (srcType)
{
case MyType_Usint: value = (uint32b)*(uint8b*)src; break;
case MyType_Uint: value = (uint32b)*(uint16b*)src; break;
case MyType_Udint: value = *(uint32b*)src; break;
case MyType_Ulint: value64 = *(uint64b*)src; break;
}
// now encode value to dest
}
我已经错误地将对齐方式uint8b Data[sizeof(uint64b)]
与src
传递了,我将解决该对齐方式。
但是在将函数调用为正确的类型(即Data
)时,我收到了强制转换encode(dest, MyType_Uint, (uint16b*)Data)
的要求,我认为这会导致更多烦人的不必要的开关。
即使在我可以访问的平台上对齐错误时,它也可以正常工作,但我不确定它如何影响另一个。
这样的铸件定位对齐和/或偏执性会否?
是的,我确实需要使用void*
参数。
答案 0 :(得分:3)
encode(dest, MyType_Uint, (uint16b*)Data)
这样的铸件定位对齐和/或偏执性会否?
这种类型的转换无法固定对齐方式和尾数。
在没有未定义行为的情况下调用该函数的方法如下:
uint16b u = some_value;
encode(dest, MyType_Uint, &u);
uint64b ul = some_other_value;
encode(dest, MyType_Ulint, &ul);
这样的演员真的有作用吗?
此类强制转换会更改表达式的类型。在这种情况下,C风格的显式转换会重新解释强制转换。
转换后的指针只能以有限的方式使用。您可以将其转换回原始类型(在您的情况下为uint8b*
)。在大多数情况下,通过指针进行的间接访问是UB,只有少数例外,其中包括指针可相互转换的对象,以及作为转换结果使用指向窄字符类型的指针。没有例外适用于您的示例,因此它将具有UB。
请注意,对于某些指针,C风格的显式转换执行静态转换,而不是重新解释转换。例如,当指针指向相同继承层次结构中的类时。这就是为什么要避免C样式转换的原因:请使用您打算使用的转换。
答案 1 :(得分:2)
投射指针永远不会固定对齐方式或字节序。它只是将地址重新解释为指向其他类型,并且标准不允许取消引用,除非相应类型的对象位于该地址。
使用可能未对齐的表示形式的一致方法是使用memcpy
:
int encode(uint8b *dest, MyType srcType, const void *src)
{
uint32b value = 0;
uint64b value64 = 0;
switch (srcType)
{
// any object can be accessed through a char pointer
case MyType_Usint: uint8b tmp8 = *(uint8b*)src; value = tmp8; break;
// use a temporary for 16 bits type (endianness question)
case MyType_Uint: uint16b tmp16; memcpy(&tmp16, src, sizeof(tmp16));
value = tmp16; break;
// directly memcpy into the value when size is the same
case MyType_Udint: memcpy(&value, src, sizeof(value)); break;
case MyType_Ulint: memcpy(&value64, src, sizeof(value64)); break;
}
// now encode value to dest
}
已知Intel类型(内核为80x86)可以容忍未对齐,但是其他处理器则不能,并且如果您尝试未对齐访问,则会引发错误。
答案 2 :(得分:1)
此代码不太可能在无法进行未对齐内存访问的平台上工作(例如ARM9 / ARM64等)。
这是因为,当您执行value64 = *(uint64b*)src
时,您希望CPU访问8字节的字(因此,它必须与64位地址对齐),并且该函数的签名不能保证这一点。
通常,在这样的平台上,调用:
char s;
int a = encode(dest, MyType_Ulint, &s);
将会编译,并且会在运行时崩溃(但可以在x86 / amd64系统上正常运行,但行为未定义)。
如果您想要便携式的东西,应该做这样的事情:
enum MyType
{
MyType_Usint,
MyType_Uint,
MyType_Udint,
MyType_Ulint
};
int encode(uint8b *dest, MyType srcType, const void *src)
{
uint32b value = 0;
uint64b value64 = 0; // This is guaranted to be aligned for 64bit access
size_t expected_operand_size[] = { 1, 2, 4, 8 };
memcpy(&value64, src, expected_operand_size[(int)srcType]);
switch (srcType)
{
case MyType_Usint: value = *(uint8b*)&value64; break;
case MyType_Uint: value = *(uint16b*)&value64; break;
case MyType_Udint: value = *(uint32b*)&value64; break;
case MyType_Ulint: break;
}
// now encode value to dest
}
顺便说一句,为方便起见,应在此处使用模板代码,并避免(无用的)复制并且易于理解(无需使用类型枚举):
template <typename T>
struct Encode
{
static uint64b convert(const void * src) { T t; memcpy(&t, src, sizeof(t)); return t; }
static uint64b convert(const T * src) { return *src; }
};
// Specialisation when we can avoid the copy
template <>
struct Encode<uint8b>
{
static uint64b convert(const void * src) { return (uint8b)*src; }
static uint64b convert(const uint8b * src) { return *src; }
};
template <typename T>
int encode(uint8b * dest, const void* src)
{
uint64b value64 = Encode<T>::convert(src);
// your encoding code here
}
// Use like this:
void * s = ...;
uint16b * s2 = ...;
uint32b * s3 = ...;
encode<uint8b>(dest, s); // No copy, calls Encode<uint32b>::do(const void*)
encode<uint16b>(dest, s2); // No copy, calls Encode<uint16b>::do(const uint16b*)
encode<uint32b>(dest, s3); // No copy, calls Encode<uint32b>::do(const uint32b*)
// or this is also safe
encode<uint16b>(dest, s); // Copy here for fixing alignment