简体   繁体   English

为什么我可以将 int 和 BOOL 转换为 void*,而不是 float?

[英]Why can I cast int and BOOL to void*, but not float?

void* is a useful feature of C and derivative languages. void*是 C 和衍生语言的一个有用的特性。 For example, it's possible to use void* to store objective-C object pointers in a C++ class.例如,可以使用void*将 objective-C object 指针存储在 C++ class 中。

I was working on a type conversion framework recently and due to time constraints was a little lazy - so I used void* ... That's how this question came up:我最近在研究一个类型转换框架,由于时间限制有点懒——所以我使用了void* ……这就是这个问题的来源:

Why can I typecast int to void*, but not float to void*?为什么我可以将 int 类型转换为 void*,但不能将 float 类型转换为 void*?

BOOL is not a C++ type. BOOL不是C ++类型。 It's probably typedef or defined somewhere, and in these cases, it would be the same as int. 它可能是typedef或在某处定义的,在这种情况下,它与int相同。 Windows, for example, has this in Windef.h: 例如,Windows在Windef.h中具有以下功能:

    typedef int                 BOOL;

so your question reduces to, why can you typecast int to void*, but not float to void*? 因此,您的问题简化为,为什么可以将int转换为void *,而不能将float转换为void *?

int to void* is ok but generally not recommended (and some compilers will warn about it) because they are inherently the same in representation. 从int到void *是可以的,但是通常不建议这样做(某些编译器会警告它),因为它们在表示上本质上是相同的。 A pointer is basically an integer that points to an address in memory. 指针基本上是一个整数,它指向内存中的地址。

float to void* is not ok because the interpretation of the float value and the actual bits representing it are different. float到void *是不正确的,因为float值的解释和表示它的实际位是不同的。 For example, if you do: 例如,如果您这样做:

   float x = 1.0;

what it does is it sets the 32 bit memory to 00 00 80 3f (the actual representation of the float value 1.0 in IEEE single precision). 它所做的是将32位存储器设置为00 00 80 3f(IEEE单精度中浮点值1.0的实际表示)。 When you cast a float to a void*, the interpretation is ambiguous. 当将浮点数转换为void *时,解释会很模糊。 Do you mean the pointer that points to location 1 in memory? 您是指指向内存中位置1的指针吗? or do you mean the pointer that points to location 3f800000 (assuming little endian) in memory? 还是您的指针指向内存中的3f800000位置(假定为小端)?

Of course, if you are sure which of the two cases you want, there is always a way to get around the problem. 当然,如果您确定要在两种情况中的哪一种情况下,总有办法解决该问题。 For example: 例如:

  void* u = (void*)((int)x);        // first case
  void* u = (void*)(((unsigned short*)(&x))[0] | (((unsigned int)((unsigned short*)(&x))[1]) << 16)); // second case

Pointers are usually represented internally by the machine as integers. 指针通常在计算机内部以整数表示。 C allows you to cast back and forth between pointer type and integer type. C允许您在指针类型和整数类型之间来回转换。 (A pointer value may be converted to an integer large enough to hold it, and back.) (指针值可以转换为足以容纳它的整数,然后返回。)

Using void* to hold integer values in unconventional. 使用void*将非常规的整数值保存。 It's not guaranteed by the language to work, but if you want to be sloppy and constrain yourself to Intel and other commonplace platforms, it will basically scrape by. 语言不能保证它能正常工作,但是如果您想草率地将自己限制在Intel和其他常见的平台上,那基本上就可以解决。

Effectively what you're doing is using void* as a generic container of however many bytes are used by the machine for pointers. 实际上,您正在执行的操作是使用void*作为通用容器,但是机器将许多字节用于指针。 This differs between 32-bit and 64-bit machines. 这在32位和64位计算机之间有所不同。 So converting long long to void* would lose bits on a 32-bit platform. 因此,将long long转换为void*将在32位平台上丢失位。

As for floating-point numbers, the intention of (void*) 10.5f is ambiguous. 对于浮点数, (void*) 10.5f的含义不明确。 Do you want to round 10.5 to an integer, then convert that to a nonsense pointer? 是否要将10.5舍入为整数,然后将其转换为无意义的指针? No, you want the bit-pattern used by the FPU to be placed into a nonsense pointer. 不,您希望将FPU使用的位模式放置在无意义的指针中。 This can be accomplished by assigning float f = 10.5f; void *vp = * (uint32_t*) &f; 这可以通过指定float f = 10.5f; void *vp = * (uint32_t*) &f;来完成float f = 10.5f; void *vp = * (uint32_t*) &f; float f = 10.5f; void *vp = * (uint32_t*) &f; , but be warned that this is just nonsense: pointers aren't generic storage for bits. ,但请注意,这只是胡说八道:指针不是位的通用存储。

The best generic storage for bits is char arrays, by the way. 顺便说一下,最好的通用存储是char数组。 The language standards guarantee that memory can be manipulated through char* . 语言标准保证可以通过char*操作内存。 But you have to mind data alignment requirements. 但是您必须注意数据对齐要求。

Standard says that 752 An integer may be converted to any pointer type . 标准752一个整数可以转换为任何指针类型 Doesn't say anything about pointer-float conversion. 什么也没说指针浮点转换。

The question is based on a false premise, namely that void * is somehow a "generic" or "catch-all" type in C or C++. It is not.这个问题基于一个错误的前提,即void *在某种程度上是 C 或 C++ 中的“通用”或“包罗万象”类型。它不是。 It is a generic object pointer type, meaning that it can safely store pointers to any type of data, but it cannot itself contain any type of data.它是一个通用的object 指针类型,这意味着它可以安全地存储指向任何类型数据的指针,但它本身不能包含任何类型的数据。

You could use a void * pointer to generically manipulate data of any type by allocating sufficient memory to hold an object of any given type, then using a void * pointer to point to it.您可以使用void *指针通过分配足够的 memory 来容纳任何给定类型的 object,然后使用void *指针指向它来一般地操作任何类型的数据。 In some cases you could also use a union, which is of course designed to be able to contain objects of multiple types.在某些情况下,您还可以使用联合,它当然被设计为能够包含多种类型的对象。

Now, because pointers can be thought of as integers (and indeed, on conventionally-addressed architectures, typically are integers) it is possible and in some circles fashionable to stuff an integer into a pointer.现在,因为指针可以被认为是整数(实际上,在传统寻址架构上,通常是整数),在某些圈子中,将 integer 填充到指针中是可能的并且很流行。 Some library API's have even documented and supported this usage — one notable example was X Windows.一些库 API 甚至记录并支持这种用法——一个著名的例子是 X Windows。

Conversions between pointers and integers are implementation-defined, and these days typically draw warnings, and so typically require an explicit cast, not so much to force the conversion as simply to silence the warning.指针和整数之间的转换是实现定义的,现在通常会发出警告,因此通常需要显式转换,与其说是强制转换,不如说是简单地消除警告。 For example, both the code fragments below print 77 , but the first one probably draws compiler warnings.例如,下面的两个代码片段都打印77 ,但第一个可能会引起编译器警告。

/* fragment 1: */
int i = 77;
void *p = i;
int j = p;
printf("%d\n", j);

/* fragment 2: */
int i = 77;
void *p = (void *)(uintptr_t)i;
int j = (int)p;
printf("%d\n", j);

In both cases, we are not really using the void * pointer p as a pointer at all: we are merely using it as a vessel for some bits.在这两种情况下,我们都没有真正将void *指针p用作指针:我们只是将它用作某些位的容器。 This relies on the fact that on a conventionally-addressed architecture, the implementation-defined behavior of a pointer/integer conversion is the obvious one, which to an assembly-language programmer or an old-school C programmer doesn't seem like a "conversion" at all.这依赖于这样一个事实,即在常规寻址的体系结构上,指针/整数转换的实现定义行为是显而易见的,对于汇编语言程序员或老派 C 程序员来说,这看起来不像是“转换”。 And if you can stuff an int into a pointer, it's not surprising if you can stuff in other integral types, like bool , as well.如果您可以将int填充到指针中,那么如果您也可以填充其他整数类型(例如bool )也就不足为奇了。

But what about trying to stuff a floating-point value into a pointer?但是尝试将浮点值填充到指针中呢? That's considerably more problematic.这问题要大得多。 Stuffing an integer value into a pointer, though implementation-defined, makes perfect sense if you're doing bare-metal programming: you're taking the numeric value of the integer, and using it as a memory address.将 integer 值填充到指针中,虽然是实现定义的,但如果您正在进行裸机编程,则非常有意义:您正在获取 integer 的数值,并将其用作 memory 地址。 But what would it mean to try to stuff a floating-point value into a pointer?但是尝试将浮点值填充到指针中意味着什么?

It's so meaningless that the C Standard doesn't even label it "undefined". C 标准甚至 label 都没有“未定义”,这毫无意义。 It's so meaningless that a typical compiler won't even attempt it.它毫无意义,典型的编译器甚至不会尝试它。 And if you think about it, it's not even obvious what it should do.如果你仔细想想,它应该做什么甚至都不明显。 Would you want to use the numeric value, or the bit pattern, as the thing to try to stuff into the pointer?您想使用数值或位模式作为尝试填充到指针中的东西吗? Stuffing in the numeric value is closer to how floating-point-to-integer conversions work, but you'd lose your fractional part.填充数值更接近于浮点到整数转换的工作方式,但您会丢失小数部分。 Using the bit pattern is what you'd probably want, but accessing the bit pattern of a floating-point value is never something that C makes easy, as generations of programmers who have attempted things like使用位模式可能是您想要的,但是访问浮点值的位模式从来都不是 C 变得容易的事情,因为几代程序员都尝试过类似的事情

uint32_t hexval = (uint32_t)3.0;

have discovered.发现了。

Nevertheless, if you were bound and determined to store a floating-point value in a void * pointer, you could probably accomplish it, using sufficiently brute-force casts, although the results are probably both undefined and machine-dependent.然而,如果您被绑定并决定将浮点值存储在void *指针中,您可能可以使用足够的强力强制转换来完成它,尽管结果可能是未定义的和机器相关的。 (That is, I think there's a strict aliasing violation here, and if pointers are bigger than floats, as of course they are on a 64-bit architecture, I think this will probably only work if the architecture is little-endian.) (也就是说,我认为这里存在严格的别名冲突,如果指针大于浮点数,当然它们在 64 位架构上,我认为这可能仅在架构为小端时才有效。)

float f = 77.75;
void *p = (void *)(uintptr_t)*(uint32_t *)&f;
float f2 = *(float *)&p;
printf("%f\n", f2);

dmr help me, this actually does print 77.75 on my machine. dmr 帮助我,这实际上在我的机器上打印了77.75

Considering any of you want you transfer float value as void * , there is a workaround using type punning.考虑到你们中的任何人都希望将float值作为void *传输,有一个使用类型双关的解决方法。

Here is an example;这是一个例子;

    struct mfloat {
        union {
            float fvalue;
            int ivalue;
        };
    };

    void print_float(void *data)
    {
        struct mfloat mf;

        mf.ivalue = (int)data;
        printf("%.2f\n", mf.fvalue);
    }


    struct mfloat mf;
    mf.fvalue = 1.99f;
    print_float((void *)(mf.ivalue));

we have used union to cast our float value(fvalue) as an integer(ivalue) to void*, and vice versa我们使用 union 将我们的浮点值(fvalue)转换为整数(ivalue)到 void*,反之亦然

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

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