当前位置: 首页 > 知识库问答 >
问题:

使用相同大小的不同类型解除引用会产生不同的结果

田冥夜
2023-03-14

为什么用于取消引用传递给printf的指针的类型会影响输出,即使类型大小相同:

void test_double(void *x)
{
    double *y = x;
    uint64_t *z = x;
    printf("double/double: %lf\n", *y);
    printf("double/uint64: %lf\n", *z);
    printf("uint64/double: 0x%016llx\n", *y);
    printf("uint64/uint64: 0x%016llx\n", *z);
}

int main(int argc, char** argv)
{    
    double x = 1.0;
    test_double(&x);
    return 0;
}

输出:

double/double: 1.000000
double/uint64: 1.000000
uint64/double: 0x00007f00e17d7000
uint64/uint64: 0x3ff0000000000000

我希望最后两行都正确打印0x3ff0000000000000,即IEEE754双浮点中1.0的表示形式。

共有1个答案

柴飞扬
2023-03-14

这是未定义的行为。C语言标准规定,如果变量参数没有格式字符串隐含的类型,那么就是UB。在第三个print语句中,您传递了一个双代码,但它期待着一个uint64_t。既然是UB,什么都可能发生。

该规范允许实现在堆栈上传递整数,但通过FPU寄存器传递浮点值,这就是我怀疑在您的测试用例中发生的事情。例如,cdecl调用x86上Linux的约定(GCC)传递x87伪堆栈上的浮点函数参数(寄存器ST0...ST7)。

如果查看生成的程序集,您可能会发现为什么第三个和第四个print语句的行为不同。在带有Clang 4.1的Mac OS X 10.8.2 64位上,我能够重现类似的结果,程序集看起来像这样,我已经注释了:

        .section        __TEXT,__text,regular,pure_instructions
        .globl  _test_double
        .align  4, 0x90
_test_double:                           ## @test_double
        .cfi_startproc
## BB#0:
        pushq   %rbp
Ltmp3:
        .cfi_def_cfa_offset 16
Ltmp4:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
Ltmp5:
        .cfi_def_cfa_register %rbp
        pushq   %rbx
        pushq   %rax
Ltmp6:
        .cfi_offset %rbx, -24

    # printf("%lf", double)
        movq    %rdi, %rbx
        movsd   (%rbx), %xmm0
        leaq    L_.str(%rip), %rdi
        movb    $1, %al
        callq   _printf

    # printf("%lf", uint64_t)
        movq    (%rbx), %rsi
        leaq    L_.str1(%rip), %rdi
        xorb    %al, %al
        callq   _printf

    # printf("%llx", double)
        leaq    L_.str2(%rip), %rdi
        movsd   (%rbx), %xmm0
        movb    $1, %al
        callq   _printf

    # printf("%llx", uint64_t)
        leaq    L_.str3(%rip), %rdi
        movq    (%rbx), %rsi
        xorb    %al, %al
        addq    $8, %rsp
        popq    %rbx
        popq    %rbp
        jmp     _printf                 ## TAILCALL
        .cfi_endproc

在打印一个double值的情况下,它将参数放入SIMD%xmm0寄存器:

movsd   (%rbx), %xmm0

但是在uint64_t值的情况下,它通过整数寄存器%rsi传递参数:

movq    (%rbx), %rsi
 类似资料:
  • 我需要编写一个java方法来从一个对象中获取特定的信息。但是,该对象可以是A类型的,也可以是B类型的。下面是我的代码的一部分: 当我这样写它时,它会引发一个错误,说“重复方法”。我怎么才能让这个起作用?

  • 我在试验inttype. h时做的简单程序: 在我的手机(64位八核ARN LTE Soc Android 10)它的工作原理很好: 但在我的计算机(64位x86 Windows 10)上,我得到: 将bool更改为uint8_t不会影响它。 编辑:我尝试用MinGW-w64 GCC C99和C17编译。

  • 这个应用程序的每个JVM应该使用相同的数据库吗?否则跟踪令牌不会在同一个应用程序中“共享”? 如何在运行传奇的相同应用程序中拆分事件?一个saga类型或saga实例是否总是在同一个应用程序上处理(直到它被关闭,所以另一个实例负责它)? 还是每个JVM都接收事件,并且每个相同类型的传奇都将运行?(并导致发送重复命令和错误) 等。还有很多问题。

  • 这是代码: 如果我在我的机器()或这里()上尝试: 相反,这里(): 这是不同的。这是由于机器厄普西隆?还是编译器精度标志?还是不同的评估? 造成这种漂移的原因是什么?问题似乎出现在函数中(因为其他值似乎相同)。

  • 问题内容: 当与MySQL数据库连接时,我有几种方法可以做同样的事情,保存或加载不同类型的参数。目前,我对每种类型都有不同的方法。如何合并这些方法,以便它们支持不同的类型? 下面是两个非常相似但使用不同类型的方法的示例: 请注意,在该示例中,类型均为数字。在类型完全不同的情况下(例如int和String),如何避免使用近乎重复的方法? 问题答案: 您可以在此处应用 策略 模式。 …