我有一个数字存储为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;
}出于某种原因,这给了我同样的正数。选演员有帮助吗?
发布于 2017-04-20 20:20:12
实际上,使用指针是一个很好的开端,但您必须先将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;
}发布于 2017-04-20 22:27:38
你可以这样做:
uint32_t u;
int32_t& s = (int32_t&) u;然后,您可以将s和u与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。(并且您可能会溢出缓冲区)。但是,可以将整数类型别名为其对应的有符号/无符号类型。
发布于 2017-04-20 18:25:26
通常,当您有两个大小相同的算术类型,并且希望使用另一个类型重新解释其中一个的位表示时,您可以使用一个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并不严格要求强制转换,但是如果没有显式编写强制转换,而且转换的类型很小,则可能会涉及“整数提升”,这通常是不可取的。)
您试图这样做的方式违反了指针混叠规则,并引发了未定义的行为。
在C++中,请注意,reinterpret_cast<> 不执行union所做的操作;当应用于算术类型时,它与static_cast<>相同。
在C++中,还请注意,上面提到的union的使用依赖于1999年C标准中的一条规则(带有更正),而这条规则还没有被正式纳入到C++标准中;但是,我熟悉的所有编译器都将按照您的预期执行。
最后,在C和C++中,long和unsigned long保证至少能够分别表示2,147,483,647. 214,7483,647和0. 4,294,967,295。您的测试程序使用了65535,这保证了long和unsigned long都是可表示的,所以不管您做什么,这个值都不会改变。嗯,除非你使用无效的指针混叠,编译器决定让恶魔飞出你的鼻子。
https://stackoverflow.com/questions/43526910
复制相似问题