简体   繁体   English

将int转换为void指针并再次返回int是否安全?

[英]Is it safe to cast an int to void pointer and back to int again?

In C and/or C++: is it safe to cast an int to void pointer and back to int again? 在C和/或C ++中:将int转换为void指针并再次返回int是否安全?

Based on the question " C++: Is it safe to cast pointer to int and later back to pointer again? ". 基于问题“ C ++:将指针转换为int并稍后再返回指针是否安全? ”。

In most modern-day commonplace machines, probably. 在大多数现代普通机器中,可能。

However, I'd bet that there is some obscure compiler or configuration (say, a 16-bit addressed machine that uses 32-bit integer arithmetic) where that is not the case. 但是,我敢打赌,有一些模糊的编译器或配置(比如,使用32位整数运算的16位寻址机器),情况并非如此。

A uintptr_t is guaranteed to hold both, though, so use that type if you want to. 但是, uintptr_t保证同时包含两者,因此如果您愿意,请使用该类型。

Here is an example where converting a pointer to an integer may not result in the same pointer when converting the integer to a pointer. 下面是一个示例,在将整数转换为指针时,将指针转换为整数可能不会产生相同的指针。

Given an architecture which has 24 bit addresses and uses two 16-bit quantities to describe the location. 给定一个具有24位地址并使用两个16位数量来描述位置的架构。 Let one quantity be the SEGMENT and the other OFFSET. 让一个数量为SEGMENT,另一个为OFFSET。 A location is designated by the notation SEGMENT:OFFSET. 位置由符号SEGMENT:OFFSET指定。

The actual 24-bit (Physical) address is calculated by: 实际的24位(物理)地址由以下公式计算:

address = segment * 16 + offset.

Using this notation, there can be more than one SEGMENT:OFFSET pair that describe the same physical address. 使用这种表示法,可以有多个描述相同物理地址的SEGMENT:OFFSET对。

When converting to an integer, a 32-bit (unsigned) quantity is used (to simplify internal calculations in the processor). 转换为整数时,使用32位(无符号)数量(以简化处理器中的内部计算)。 The problem is how to convert the physical address into the same SEGMENT::OFFSET that was used in the creation of the physical address. 问题是如何将物理地址转换为在创建物理地址时使用的相同SEGMENT :: OFFSET。

A generic equation for converting integer to pointer is: 将整数转换为指针的通用公式为:

offset = address & 0xFFFF; // Mask off high order bits, keep lower 16.
segment = address >> 16;   // Shift address right 16 bits, zero fill.

Although the physical address of this new segment and offset is equal to the physical address of the original SEGMENT:OFFSET, the segments and offsets are not guaranteed to be the same. 虽然这个新段的物理地址和偏移量等于原始SEGMENT:OFFSET的物理地址,但不保证段和偏移量是相同的。

To optimize code, there are processor instructions that use relative addressing in a segment. 为了优化代码,有一些处理器指令在段中使用相对寻址。 These instructions may get messed up when the SEGMENT value changes due to conversion from a physical address. 当SEGMENT值因物理地址转换而发生变化时,这些指令可能会混乱。

In this scenario, converting from a pointer to an integer is possible. 在这种情况下,可以从指针转换为整数。 HOWEVER, converting from the integer to the pointer IS STRONGLY DISCOURAGED. 但是,从整数到指针的转换是强烈的冒充。 Hard to debug errors could occur during run-time. 在运行时可能会发生难以调试的错误。

Bonus question: Can you name the actual architecture? 奖金问题:你能说出实际的架构吗?

Why would you want to do this? 你为什么想做这个?

Reply for C, I don't know enough about C++ for that: No, behavior is not defined to cast an int to void* . 对C的回复,我对C ++的了解不够:不,没有定义行为将intvoid* First of all you should always use uintptr_t if you have it for such a thing. 首先,如果你有这样的东西,你应该总是使用uintptr_t Using int is an abuse. 使用int是一种滥用行为。

Then, C does not guarantee anything if your uintptr_t doesn't come from a valid address. 然后,如果您的uintptr_t不是来自有效地址,则C不保证任何内容。 It only guarantees the other way round. 它只能保证反过来。 Don't do it. 不要这样做。

Edit: Here is the relevant part of the C99 standard. 编辑:这是C99标准的相关部分。 As you can see all alarms can go off... 正如您所看到的,所有警报都可以消失......

An integer may be converted to any pointer type. 整数可以转换为任何指针类型。 Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation 除非先前指定,否则结果是实现定义的,可能未正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示

The last is particularly embarrassing since this means that the pointer value that is such obtained can not be used anymore, until it is overwritten: 最后一个特别令人尴尬,因为这意味着不能再使用这样获得的指针值,直到它被覆盖:

Certain object representations need not represent a value of the object type. 某些对象表示不需要表示对象类型的值。 If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. 如果对象的存储值具有这样的表示并且由不具有字符类型的左值表达式读取,则行为是未定义的。 ... Such a representation is called a trap representation . ......这种表示称为陷阱表示

No. A void pointer is no different from any other pointer with respect to size. 不可以。无效指针与任何其他指针的大小没有区别。 Hence it will run into exactly the same types of issues as other pointer types. 因此,它将遇到与其他指针类型完全相同类型的问题。

It's implementation defined just like the last question and for the same reason. 它的实现定义与上一个问题一样,并且出于同样的原因。 It's less likely to result in misbehavior but it's still implementation defined. 它不太可能导致不良行为,但它仍然是实施定义的。

No. There might be certain circumstances where it appears to work for a certain compiler&settings, and then two years later you spend weeks debugging that something changed and the conversion no longer works as expected. 不会。在某些情况下,它似乎适用于某些编译器和设置,然后两年后,您花费数周时间调试某些内容发生了变化,转换不再按预期工作。

If you just design your code in a way that doesn't need this sort of behavior (best case avoids use of such conversion at all, worst case use char[] ) then you won't have to worry about obscure bugs in the future. 如果你只是以不需要这种行为的方式设计你的代码(最好的情况是避免使用这种转换,最坏的情况下使用char[] )那么你将不必担心将来会出现模糊的错误。

Not necessarily. 不必要。 Depends on the size of a pointer. 取决于指针的大小。 Why would you want to do this? 你为什么想做这个?

If the range of your integers is fairly small, you could always do something like: 如果你的整数范围相当小,你总是可以这样做:

static const char dummy[MAXVAL];

and then use dummy+i as a way of encoding i as a pointer. 然后使用dummy+i作为将i编码为指针的方式。 This is 100% portable. 这是100%便携式。 If you only care that it's 99% portable, you could use (char *)0 + i . 如果您只关心它是99%便携式,您可以使用(char *)0 + i

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

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