根据类型大小增加void*的正确方法,因为强制转换导致临时值,而不是左值

Proper method to increment a void* by type size because cast results in temporary, not lvalue

本文关键字:转换 因为 void 增加 方法 类型      更新时间:2023-10-16

在升级以下旧c++代码以在现代Xcode 4下编译时。在x编译器中,我发现最初的开发人员使用了一种可以工作但应该避免的编程习惯用法——现在是一个错误:

*((USHORT*) pvPixel)++ = uRG;   // copy red & green (2 bytes)

我看到了意图:将unsigned short (uRG)复制到由void指针(pvPixel)指向的地址,然后增加适当的大小(在上面的情况下,2字节)。问题是,转换pvPixel会导致一个临时值,而不是左值,这是不允许的。

最初的开发人员在几十个地方使用了这个习语——我可以在短时间内重写。但是,最好提供一个优雅且可读的解决方案,而不是强行重写每个事件。我可以想到几种可能的替代方法,以暴力重写每个事件:宏,内联函数,也许是模板?

我的问题是:是否有任何c++语法/语言解决这个问题?什么样的方法可以为未来的开发人员提供最清晰的代码?

(下面两个示例函数):

void PrimSurfaceGDI3::mFillHLine( UINT uRGB, UINT uX, UINT uY, UINT uW )
{
    LPVOID pvPixel;
    if ( uW > 0 )
    {
        //  obtain a pointer to a specified pixel in the surface
        pvPixel = mPtr( uX, uY );
        USHORT uRG = *(USHORT*) &uRGB;
        BYTE uB = ((BYTE*) &uRGB)[2];
        LPVOID pvEnd = (BYTE*) pvPixel + uW * 3;
        while (pvPixel < pvEnd)
        {
            // The two lines below are now ILLEGAL in modern compilers because casting pvPixel to USHORT* or BYTE* results in a TEMPORARY, not an lvalue
            *((USHORT*) pvPixel)++ = uRG;   // copy red & green (2 bytes)
            *((BYTE*) pvPixel)++ = uB;      // copy blue (1 byte)
        }
    }
}

以下,这种习惯用法用于for循环的重新初始化语句:

void PrimSurfaceGDI3::mFillVLine( UINT uRGB, UINT uX, UINT uY, UINT uH )
{
    LPVOID pvPixel = mPtr( uX, uY );
    USHORT uRG = *(USHORT*) &uRGB;
    BYTE uB = ((BYTE*) &uRGB)[2];
    LPVOID pvEnd = (BYTE*) pvPixel + uH * muScan;
    // The reinitialization statement is now ILLEGAL in modern compilers because casting pvPixel to BYTE* results in a TEMPORARY, not an lvalue
    for ( ; pvPixel < pvEnd; ((BYTE*) pvPixel) += muScan)
    {
        *(USHORT*) pvPixel = uRG;       // copy red & green (2 bytes)
        ((BYTE*) pvPixel)[2] = uB;      // copy blue (1 byte)
    }
}

解决这个问题的一个更合理的方法可能是稍微重做。改变mPtr,使其返回一个指针(或容器引用)到一个三字节类型,其中有三个字段,然后使用std::fill_n填充输入RGB。然后函数收缩成这样(这给编译器一个很好的机会来应用任何适当的优化):

void PrimSurfaceGDI3::mFillHLine( UINT uRGB, UINT uX, UINT uY, UINT uW )
{
    if ( uW > 0 )
    {
        ColorRep pvPixel* = mPtr( uX, uY );
        std::fill_n(pvPixel, uW, ColorRep(uRGB));
    }
}

接下来,注意最初的"优化"很可能是一个反优化,因为它在程序中引入了所有不对齐的双字节访问。

如果你想在代码中尽可能少地改变,你接受这段代码可能不能在x86以外的任何架构上工作(由于不对齐的访问),并且你愿意用-fno-strict-aliasing编译(这可能是由于强制转换为不相关的类型所必需的),那么你可能可以用reinterpret_cast来引用:

*(reinterpret_cast<unsigned short*&>(pvPixel))++ = uRG;
相关文章: