【问题标题】:Why must I use the structure dereference operator rather than just cast a union为什么我必须使用结构取消引用运算符而不是仅仅强制转换
【发布时间】:2021-03-24 17:53:54
【问题描述】:

为什么以下两个对f 的调用会给出不同的输出?

#include"stdio.h"

typedef union {
  int value_int;
  char value_char;
} my_union_t;

void f(int x)
{
  printf("x: %d\n", x);
}

void main()
{
  my_union_t u;
  u.value_int = 7;
  my_union_t * p_u = &u;
  f( (int) p_u );
  f( p_u->value_int );
}

输出:

x: -393095784
x: 7

【问题讨论】:

  • 第一个是变量的地址,第二个是一个成员的值。
  • 你没有“铸造工会”,因为那是不可能的。您已将指针转换为联合。

标签: c unions dereference


【解决方案1】:

(int) p_u 获取一个指向联合的指针,该指针是一个地址,并将其转换为 int。您的输出看起来完全符合我的预期。 -393095784 是某个指针(RAM 内存中的变量地址)的有符号整数表示。

p_u->value_int 从联合中读取value_int,看起来完全符合我的预期。还要知道p_u->value_int 完全等同于(*p_u).value_int* 被称为“取消引用运算符”。它读取指针的“内容”,或者换句话说,获取存储在指针指向的地址中的内容。 *p_u 的意思是“读取p_u 指针指向的内容”,其中的内容就是联合体本身。 some_ptr->(*some_ptr). 的简写。

请注意,当您将指针地址转换为int 时,指针地址会从其无符号值溢出到有符号值。如果你没有将它转换为 int,它会变成这样:

Address, assuming your hardware has 64-bit addresses
AND the address didn't roll over more than once when
you originally cast it into an `int` type:
18446744073316455832
0xFFFFFFFFE891D598

Address, assuming your hardware has 32-bit addresses
AND the address didn't roll over more than once when
you originally cast it into an `int` type:
3901871512
0xE891D598

Address, **using the exact pointer size for the hardware
this code is actually being run on!**:
0xffffffffe891d598

sizeof(pointer) on this hardware architecture =
sizeof(void*) = 8 bytes = 64 bits.

正如您在上面看到的,64 位地址打印输出(第一个块)和“精确指针大小”打印输出(第三个块)是相同的地址。这意味着我运行程序的硬件架构使用 64 位指针。通过打印sizeof(void*) 来获取任何指针的大小也可以很容易地看到这一点,我也这样做了。 请注意,这里使用void* 只是为了方便。实际上,您可以在那里使用任何指针类型或实际指针变量,因为指针的大小对于给定硬件架构上的所有指针类型都是相同的。

上面的输出是由这个程序产生的,你可以在这里实时运行:https://onlinegdb.com/r1YSjZt4_:

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

int main()
{
    printf("\nAddress, assuming your hardware has 64-bit addresses\n"
           "AND the address didn't roll over more than once when\n"
           "you originally cast it into an `int` type:\n");
    printf("%" PRIu64 "\n", (uint64_t)-393095784);
    printf("0x%" PRIX64 "\n", (uint64_t)-393095784);
    
    printf("\nAddress, assuming your hardware has 32-bit addresses\n"
           "AND the address didn't roll over more than once when\n"
           "you originally cast it into an `int` type:\n");
    printf("%" PRIu32 "\n", (uint32_t)-393095784);
    printf("0x%" PRIX32 "\n", (uint32_t)-393095784);
    
    printf("\nAddress, **using the exact pointer size for the hardware\n"
           "this code is actually being run on!**:\n");
    printf("%p\n", (void *)-393095784);
    
    printf("\nsizeof(pointer) on this hardware architecture =\n"
           "sizeof(void*) = %zu bytes = %zu bits.\n", 
            sizeof(void*), sizeof(void*)*8);
    
    return 0;
}

另请参阅您的问题下的 cmets。

参考资料:

  1. http://www.cplusplus.com/reference/cstdio/printf/https://en.cppreference.com/w/cpp/io/c/fprintf
  2. http://www.cplusplus.com/reference/cstdint/
  3. http://www.cplusplus.com/reference/cinttypes/

【讨论】:

  • 请注意,%lu 在 MS Visual C 中需要一个 32 位的值,而不是 64。指针值应该用%p 打印,所以在这种情况下是printf("%p\n", (void*)-393095784);
  • @WeatherVane,好点子!为了考虑不同的架构,我更新了代码以使用来自&lt;inttypes.h&gt;PRIxN 打印说明符,它们与硬件无关。然后我还添加了第三个打印来打印指针的大小,如您在评论中显示的那样。谢谢。
猜你喜欢
  • 1970-01-01
  • 2020-08-10
  • 2015-07-06
  • 2014-07-02
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2020-09-03
相关资源
最近更新 更多