在将指针作为void *参数的参数传递时强制转换指针

时间:2019-06-05 11:57:32

标签: c++

具有以下功能:

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*参数。

3 个答案:

答案 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