简体   繁体   English

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

[英]Casting a pointer when passing it as an argument for void* parameter

Lets have following function: 让我们具有以下功能:

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
}

I have passed wrongly aligned uint8b Data[sizeof(uint64b)] as src , I will fix the alignment. 我将错误对齐的uint8b Data[sizeof(uint64b)]传递为src ,我将修复对齐问题。

But I received a demand to cast Data when calling the function to proper type, ie encode(dest, MyType_Uint, (uint16b*)Data) , which I think would cause in some more annoying unnecessary switches. 但是我收到一个需求蒙上Data调用函数要有合适类型,即当encode(dest, MyType_Uint, (uint16b*)Data) ,我认为这会导致一些更讨厌不必要的开关。 It's working even with the wrong alignment on platforms accessible for me, but I'm not sure, how it affects the other. 即使在我可以访问的平台上对齐不正确,它也可以正常工作,但是我不确定它如何影响其他平台。

Would such cast fix alignment and/or endianess? 这样的演员表会修正对齐方式和/或偏爱度吗?

And yes, I really need the void* parameter. 是的,我真的需要void*参数。

 encode(dest, MyType_Uint, (uint16b*)Data) 

Would such cast fix alignment and/or endianess? 这样的演员表会修正对齐方式和/或偏爱度吗?

Such cast does not fix alignment nor endiannes. 这样的转换不能固定对齐方式和尾数。

The way to call that function without undefined behaviour is following: 在没有未定义行为的情况下调用该函数的方法如下:

uint16b u = some_value;
encode(dest, MyType_Uint, &u);

uint64b ul = some_other_value;
encode(dest, MyType_Ulint, &ul);

Does such cast actually do anything? 这样的演员真的有什么作用吗?

Such cast changes the type of the expression. 这种强制转换会更改表达式的类型。 In this case, the C style explicit conversion does reinterpret cast. 在这种情况下,C风格的显式转换会重新解释强制转换。

The converted pointer can be used only in limited ways. 转换后的指针只能以有限的方式使用。 You can convert it back to the original type ( uint8b* in your case). 您可以将其转换回原始类型(在您的情况下为uint8b* )。 Indirection through the pointer is UB in most cases, with few exceptions, which include pointer-interconvertible objects, as well as using a pointer to a narrow character type as the result of the conversion. 在大多数情况下,通过指针进行的间接访问是UB,只有少数例外,其中包括指针可相互转换的对象,以及作为转换结果使用指向窄字符类型的指针。 There is no exception that would apply to your example, so it would have UB. 没有例外适用于您的示例,因此它将具有UB。

Note that for some pointers, the C style explicit conversion does static cast, rather than reinterpret cast. 请注意,对于某些指针,C风格的显式转换执行静态转换,而不是重新解释转换。 For example, when the pointers are to classes in the same inheritance hierarchy. 例如,当指针指向相同继承层次结构中的类时。 This is why C style casts are to be avoided: Use the cast that you intend to use. 这就是避免使用C样式强制转换的原因:使用您打算使用的强制转换。

Casting a pointer never fix alignment nor endianness. 强制转换指针永远不会修复对齐方式或字节顺序。 It just re-interpret the address as pointing to a different type, and standard does not allow to de-reference it unless an object of the appropriate type lies at that address. 它只是将地址重新解释为指向其他类型,并且标准不允许取消引用,除非相应类型的对象位于该地址。

The conformant way to use a possibly misaligned representation is to use memcpy : 使用可能未对齐的表示形式的一致方法是使用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 type (80x86 to core) are known to be tolerant to misalignment, but other processors are not and raise an error if you attempt a misaligned access. 已知Intel类型(核心为80x86)可以容忍未对准,但其他处理器则不能,并且如果您尝试未对准访问,则会引发错误。

This code is unlikely to work on platform that can't do unaligned memory access (like ARM9/ARM64 etc...). 此代码不太可能在无法进行未对齐内存访问的平台上工作(例如ARM9 / ARM64等)。 This is because when you do value64 = *(uint64b*)src you expect the CPU to access a 8 bytes word (and as such it must be aligned to 64bit address) and the signature of the function does not guaranty this. 这是因为当您执行value64 = *(uint64b*)src您希望CPU访问一个8字节的字(因此,它必须与64位地址对齐),并且该函数的签名不能保证这一点。

Typically, on such platform, calling: 通常,在这样的平台上,调用:

  char s;
  int a = encode(dest, MyType_Ulint, &s); 

will compile, and will crash at runtime (but will run ok on x86/amd64 system, yet with undefined behavior). 将编译,并在运行时崩溃(但可以在x86 / amd64系统上正常运行,但行为未定义)。

If you want something portable, you should do something like this: 如果您想要便携式的东西,应该做这样的事情:

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
}

By the way, you should use template code here for convenience and to avoid the (useless) copy and be easier to understand (no need to have an type enum) : 顺便说一句,您应该在此处使用模板代码,以方便使用,并避免(无用的)复制并且易于理解(无需使用类型枚举):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM