【问题标题】:C++ Change only lower 32 bits of an __int64 variableC++ 仅更改 __int64 变量的低 32 位
【发布时间】:2020-03-08 11:15:00
【问题描述】:

我有一个带有随机值的 int64 变量。我想将它的低 32 位修改为 0xf0ffffff

变量是 rdx 寄存器,但我想编辑 edx 值

ContextRecord->Rdx = 0xf0ffffff; // Not correct

【问题讨论】:

标签: c++


【解决方案1】:

变量是 rdx 寄存器,但我想编辑 edx 值

我假设这意味着您希望保持 most significant 32 位不变,并且只更改最低有效的 32 位。

假设数据成员ContextRecord->Rdx包含原始值,并且您想将编辑后的值写回该数据成员,则可以使用以下代码:

auto temp = ContextRecord->Rdx;
temp &= 0xffffffff00000000; //set least significant 32-bits to 00000000
temp |= 0x00000000f0ffffff; //set least significant 32-bits to f0ffffff
ContextRecord->Rdx = temp;

当然,这些行可以合并成一行,像这样:

ContextRecord->Rdx = ContextRecord->Rdx & 0xffffffff00000000 | 0x00000000f0ffffff;

请注意,此行仅适用于&operator precedence 高于|,否则需要额外的括号。

【讨论】:

    【解决方案2】:

    读取整个值,屏蔽低位,然后将其与您想要的 32 位值按位或:

    #include <stdint.h>
    
    void f(int64_t *X)
    {
        *X = (*X & ~(uint64_t)0xffffffff) //mask out the lower 32 bits
             | 0xf0ffffff; //<value to set into the lower 32 bits
    }
    

    gccclang 在 little-endian 架构上将其优化为直接将 mov 放入低 32 位,即相当于:

    #include <string.h>
    
    //only works on little-endian architectures
    void g(int64_t *X)
    {
        uint32_t y = 0xf0ffffff;
        memcpy(X,&y,sizeof(y));
    }
    

    https://gcc.godbolt.org/z/nkMSvw

    【讨论】:

      【解决方案3】:

      如果你在直接组装中这样做,你可以

      mov edx 0xf0ffffff
      

      因为 edx 是 rdx 低 32 位的别名。由于您似乎想在 C/C++ 中执行此操作,因此您需要直接调整 Rdx。有点像 -

      CONTEXT ctx;
      GetThreadContext(hYourThread,&ctx);  // check return value, handle errors
      DWORD64 newRdx = ctx->Rdx;
      newRdx &=  0xfffffffff0ffffff;
      newRdx |=  0xf0ffffff;
      

      【讨论】:

        【解决方案4】:

        需要编写一些单元测试,因为我没有针对所有架构中的所有类型进行测试,但可能是您正在寻找的类似下面的模板

        #include <cassert>
        #include <cstdint>
        #include <iostream>
        #include <limits>
        #include <type_traits>
        
        
        template <const int bits, class /* This is where I wish concepts were completed */ T>
        constexpr T modifyBits(T highPart, T lowPart)
        {
            // std::numeric_limits<T>::max() will fail on bits == 0 or float types
            static_assert(bits != 0);
            static_assert(std::is_signed<T>::value || std::is_unsigned<T>::value);
        
            constexpr T almostallSetMask = std::numeric_limits<T>::max();
            constexpr T bitmaskRaw = almostallSetMask >> (bits - (std::is_signed<T>::value ? 1 : 0));
            constexpr T bitmaskHigh = bitmaskRaw << bits;
            constexpr T bitmaskLow = ~bitmaskHigh;
        
            return (highPart & bitmaskHigh) | (lowPart & bitmaskLow);
        }
        
        int main()
        {
            // Example usage
            constexpr int64_t value = 0xFFFFFFFF00000000LL;
            constexpr int64_t updated = modifyBits<32, int64_t>(value, 0xFFFFFFFFLL);
            static_assert(updated == -1LL); // has to pass
        
            return 0;
        }
        

        如您所见,我可以像这样以通用方式使用静态断言和 const_expr。座右铭是:一次编写,到处使用。但是请注意,如果没有单元测试,这根本不完整。如果您喜欢,请随意复制,您可以将其视为 CC0 或公共域,

        【讨论】:

        • 谢谢,但我处于内核模式
        【解决方案5】:

        一种稍微有点欺骗性和不那么值得推荐的方法是类型双关语:

        struct splitBytes {
            __int32 lower, upper;
        }
        
        void changeLower(__int64* num) {
            splitBytes* pun = (splitBytes*)*num;
            pun->lower = 0xf0ffffff;
        }
        

        注意:类型双关语非常危险,因此除非不可避免,否则您真的不应该使用。它基本上可以让您将一块内存视为不同类型的内存。真的,如果可以避免,请不要使用它。我只是把它放在那里。

        【讨论】:

        • 如果我没记错的话,贴出的代码违反了strict aliasing rule,至少理论上可以导致undefined behavior
        • @AndreasWenzel 简短回答:是的。长答案:这取决于(你没想到,对吧?)。基本上,如果以下任何一种情况都会出现问题: punned type 和 punning type 具有不同的大小;至少一个双关类型是一个类;算术是用指针完成的。这就是为什么它应该几乎永远不会完成。
        • 您所说的目前可能是真的。然而,编译器优化正变得越来越激进。每当编译器遇到导致未定义行为的代码时,它可以将该代码视为不可访问,这意味着编译器可以简单地对其进行优化。有关该主题的更多信息,请参阅this very interesting answer by Microsoft Blogger Raymond Chen
        • @AndreasWenzel 哦,我完全没有意识到这一点。答案内容丰富,值得一读。谢谢!
        猜你喜欢
        • 2011-02-11
        • 1970-01-01
        • 2010-12-20
        • 2010-10-31
        • 1970-01-01
        • 2011-02-08
        • 2015-09-10
        • 1970-01-01
        • 2018-04-09
        相关资源
        最近更新 更多