如果`uintptr_t` 可以用来存储指针和数字,为什么`void*` 不能做同样的事情?

如果`uintptr_t` 可以用来存储指针和数字,为什么`void*` 不能做同样的事情?

问题描述:

语言中的枚举,例如Swift 或 Rust 支持一种混合的选择数据";机制,这样我就可以定义一个类型,如:

Enumerations in languages like e.g. Swift or Rust support a kind of hybrid "choice plus data" mechanism, such that I could define a type like:

enum SomeOption {
  None,
  Index(int),
  Key(string),
  Callback(fn),
}

现在,如果我要在 C 中实现它,我的理解是这样的事情是有效的:

Now if I were to implement this in C, my understanding is that something like this would not be valid:

typedef enum {
  is_callback_or_none,
  is_string,
  is_number
} my_choice;

typedef struct {
   my_choice value_type;
   void* value;
} my_option;

my_option x = {
  .value_type = is_number,
  .value = (void*)42
};
if (x.value_type == is_number) {
  int n = (int)x.value;
  // … use n…
}

我不确定这样做到底要冒什么风险,但根据例如指针可以存储值吗空指针的用途是什么? 我应该在 void* 中存储的唯一内容是实际地址和 NULL.[旁白:请对在 void* 中存储回调 函数 指针的单独问题视而不见,我忘记了它是 有问题 当我编写这个例子时.]

I'm not sure what exactly I risk in doing this, but according to e.g. Can pointers store values and what is the use of void pointers? the only things I should store in a void* are actual addresses and NULL. [Aside: please turn a blind eye to the separate question of storing callback function pointers in a void* which I forgot was problematic when I made up this example.]

我认为更合适的方法是使用联合,例如:

I suppose a more proper way to do this would be to use a union, e.g.:

typedef struct {
   my_choice value_type;
   union {
      int number_value;
      char* string_value;
      void* pointer_value;
   };
} my_option;

……无论如何,这可能更好.但是我特别想知道 void* value 版本的无效性.如果我(而不是 union 解决方案)简单地用 uintptr_t 代替 void* 会怎样?

…which is probably nicer all around anyway. But I'm wondering specifically about the invalidity of the void* value version . What if I were (instead of the union solution) to simply substitute uintptr_t in place of the void*?

typedef struct {
   my_choice value_type;
   uintptr_t value;
} my_option;

在此结构的 uintptr_t value 字段中存储或者一个指向字符串/回调/null的指针一个数字是否有效并且[至少 POSIX-] 可移植代码?如果是这样,为什么可以,但不是看似等效的 void* value 版本?

Would storing either a pointer to a string/callback/null or a number within the uintptr_t value field of this struct be valid and [at least POSIX-]portable code? And if so, why is that okay, but not the seemingly equivalent void* value version?

即使 void * 值表示为数字,也不意味着编译器会像处理数字一样处理它们.

Even if void * values are represented as numbers, that does not mean the compiler handles them as it does numbers.

A uintptr_t 是一个数字;C 2018 7.20.1.4 1 说它指定了一个无符号整数类型.所以它的行为就像其他无符号整数类型:您可以将任何数字放入其范围内并返回相同的数字(并且您可以用它进行算术运算).该段落进一步说任何有效的 void * 都可以转换为 uintptr_t 并且将其转换回来将产生原始指针(或等效的东西,例如指向同一位置的指针但具有不同的表示).因此,您可以将指针存储在 uintptr_t 对象中.

A uintptr_t is a number; C 2018 7.20.1.4 1 says it designates an unsigned integer type. So it behaves like other unsigned integer types: You can put any number within its range and get the same number back (and you can do arithmetic with it). The paragraph further says any valid void * can be converted to uintptr_t and that converting it back will produce the original pointer (or something equivalent, such as a pointer to the same place but with a different representation). So you can store pointers in uintptr_t objects.

然而,C 标准并没有规定可以将一系列数字放入 void * 并取回它们.6.3.2.3 5 表示当整数转换为指针类型时,结果是实现定义的(除了将常量零转换为 void * 会产生空指针,根据 6.3.2.3 3).6.3.2.3 6 表示当您将指针转换为整数时,结果是实现定义的.(7.20.1.4 当数字是一个最初来自指针的 uintptr_t 时会覆盖它.)

However, the C standard does not say there is a range of numbers you can put into void * and get them back. 6.3.2.3 5 says that when an integer is converted to a pointer type, the result is implementation-defined (except that converting a constant zero to void * yields a null pointer, per 6.3.2.3 3). 6.3.2.3 6 says when you convert a pointer to an integer, the result is implementation-defined. (7.20.1.4 overrides this when the number is a uintptr_t that came from a pointer originally.)

那么,如果您将一个数字存储在 void * 中,您怎么知道它会起作用?C 标准不向您保证它会起作用.您需要一些说明编译器可以工作的文档.

So, if you store a number in a void *, how do you know it will work? The C standard does not guarantee to you that it will work. You would need some documentation for the compiler that says it will work.