将 ulong 转换为 long

Converting a ulong to a long

我有一个存储为 ulong 的号码。我希望以 2 的补码方式解释存储在内存中的位。所以我希望第一位是符号位等。如果我想转换为长整数,以便将数字正确解释为 2 的补码,我该怎么做?

我尝试创建不同数据类型的指针,它们都指向同一个缓冲区。然后我将 ulong 存储到缓冲区中。然后我取消引用了一个长指针。然而,这给我带来了不好的结果?

我做到了:

#include <iostream>
using namespace std;

int main() {
    unsigned char converter_buffer[4];//  

    unsigned long       *pulong;
    long                *plong;


    pulong = (unsigned long*)&converter_buffer;
    plong  =  (long*)&converter_buffer;

    unsigned long ulong_num = 65535; // this has a 1 as the first bit

    *pulong = ulong_num;

    std:: cout << "the number as a long is" << *plong << std::endl;
    return 0;
}

出于某种原因,这给了我相同的正数。 铸造有帮助吗?

一般来说,当你有两个大小相同的算术类型并且你想使用另一个的类型重新解释一个的位表示时,你可以使用 union:

#include <stdint.h>

union reinterpret_u64_d_union {
    uint64_t u64;
    double   d;
};

double
reinterpret_u64_as_double(uint64_t v)
{
    union reinterpret_u64_d_union u;
    u.u64 = v;
    return u.d;
}

然而,对于将无符号数转换为具有相同大小的有符号类型(反之亦然)的特殊情况,您可以只使用传统的转换:

int64_t
reinterpret_u64_as_i64(uint64_t v)
{
    return (int64_t)v;
}

[u]int64_t 并不严格要求强制转换,但如果您没有显式编写强制转换,并且您正在转换的类型很小,则 "integer promotions" 可能会涉及,这通常是不可取的。)

您尝试这样做的方式违反了指针别名规则并引发了未定义的行为。

在 C++ 中,请注意 reinterpret_cast<> 不会 union 做的事情;当应用于算术类型时,它与 static_cast<> 相同。

在 C++ 中,还要注意上面 union 的使用依赖于 1999 C 标准(带更正)中的规则 没有 我上次检查时已正式纳入 C++ 标准;但是,我熟悉的所有编译器都会按照您的期望进行操作。

最后,在 C 和 C++ 中,longunsigned long 保证至少能够表示 −2,147,483,647 ... 214 ,7483,647 和 0 ... 4,294,967,295,分别。您的测试程序使用了 65535,保证可以用 longunsigned long 表示,因此无论您如何操作,该值都不会改变。好吧,除非你使用了无效的指针别名并且编译器决定让恶魔从你的鼻子里飞出来。

实际上使用指针是一个好的开始,但您必须首先将 unsigned long* 转换为 void*,然后您可以将结果转换为 long* 并取消引用它:

#include <iostream>
#include <climits>

int main() {
    unsigned long ulongValue = ULONG_MAX;
    long longValue = *((long*)((void*)&ulongValue));

    std::cout << "ulongValue: " << ulongValue << std::endl;
    std::cout << "longValue:  " << longValue << std::endl;

    return 0;
}

上面的代码将产生以下结果:

ulongValue: 18446744073709551615
longValue:  -1

使用模板可以使其在代码中更具可读性:

#include <iostream>
#include <climits>

template<typename T, typename U>
T unsafe_cast(const U& from) {
    return *((T*)((void*)&from));
}

int main() {
    unsigned long ulongValue = ULONG_MAX;
    long longValue = unsafe_cast<long>(ulongValue);

    std::cout << "ulongValue: " << ulongValue << std::endl;
    std::cout << "longValue:  " << longValue << std::endl;

    return 0;
}

请记住,此解决方案绝对不安全,因为您可以将任何内容转换为 void*。这种做法在 C 中很常见,但我不建议在 C++ 中使用它。考虑以下情况:

#include <iostream>

template<typename T, typename U>
T unsafe_cast(const U& from) {
    return *((T*)((void*)&from));
}

int main() {
    std::cout << std::hex << std::showbase;

    float fValue = 3.14;
    int iValue = unsafe_cast<int>(fValue); // OK, they have same size.

    std::cout << "Hexadecimal representation of " << fValue
              << " is: " << iValue << std::endl;
    std::cout << "Converting back to float results: "
              << unsafe_cast<float>(iValue) << std::endl;

    double dValue = 3.1415926535;
    int lossyValue = unsafe_cast<int>(dValue); // Bad, they have different size.

    std::cout << "Lossy hexadecimal representation of " << dValue
              << " is: " << lossyValue << std::endl;
    std::cout << "Converting back to double results: "
              << unsafe_cast<double>(lossyValue) << std::endl;

    return 0;
}

上面的代码对我的结果如下:

Hexadecimal representation of 3.14 is: 0x4048f5c3
Converting back to float results: 3.14
Lossy hexadecimal representation of 3.14159 is: 0x54411744
Converting back to double results: 6.98387e-315

最后一行你可以得到任何东西,因为转换将从内存中读取垃圾。

编辑

正如lorro在下面评论的那样,使用memcpy()更安全并且可以防止溢出。所以,这是另一个更安全的类型转换版本:

template<typename T, typename U>
T safer_cast(const U& from) {
    T to;
    memcpy(&to, &from, (sizeof(T) > sizeof(U) ? sizeof(U) : sizeof(T)));
    return to;
}

你可以这样做:

uint32_t u;
int32_t& s = (int32_t&) u;

那么你可以将su与2的补码互换使用,例如:

s = -1;
std::cout << u << '\n';     // 4294967295

在您的问题中,您问的是 65535,但这是一个正数。你可以这样做:

uint16_t u;
int16_t& s = (int16_t&) u;

u = 65535;
std::cout << s << '\n';    // -1

请注意,将 65535(正数)分配给 int16_t 将实现定义的行为,它不一定会给出 -1

您的原始代码的问题是不允许将 char 缓冲区别名为 long。 (并且您可能会溢出缓冲区)。但是,可以将整数类型别名作为其对应的 signed/unsigned 类型。