int指针和浮点指针之间的奇怪区别

Tra*_*acy 2 c floating-point int printf pointers

请看下面的代码

#include <stdio.h>
#include <stddef.h>

typedef struct _node
{
int a;
char *s;
}Node, *nodePtr;

int main(int argc, char *argv[])
{
char *str = "string"; /*str points to satic storage area*/
Node nd;
nodePtr pNode = NULL;
size_t offset_of_s = offsetof(Node,s);

nd.a = 1;
nd.s = str;

pNode = &nd;

    /*Get addr of s, cast it to a different data types pointer, then de-reference it*/

/*this works, print "string"*/
printf("%s\n", *(int*)((char*)pNode + offset_of_s));

/*this sucks, print (null)*/
printf("%s\n", *(float*)((char*)pNode + offset_of_s));

return 0;
} 
Run Code Online (Sandbox Code Playgroud)

我试图获取Node结构的s成员的地址,其转换为不少于4个字节的数据类型(4个字节是我机器上指针的宽度),然后取消引用指针作为参数printf.

我认为两个printfs的结果应该相同,但第二个显示"(null)".

float和int在我的机器上有相同的字节宽度,是导致这种情况的两种类型的内部不同表示吗?

提前致谢 !

Cub*_*bbi 6

您的程序调用未定义的行为,因为参数的类型printf()不是printf所期望的.通过查看源代码无法预测结果.

C99-TC3, §7.19.6.1/9

如果任何参数不是相应转换规范的正确类型,则行为未定义.

但是,如果您对所观察到的行为原因感兴趣,那么您的编译器可能是将浮点值传递给浮点CPU寄存器中的printf()的编译器之一.(例如,GNU和CLang这样做).对printf的第二次调用将取消引用的值放在浮点寄存器中,但是printf,看到%s转换说明符,查看char*将传递的寄存器,可能是通用寄存器,在您的情况下恰好为零.

PS:这是GCC 4.6.1在我的Linux上做出的

main:
    pushq   %rbx
    leal    .LC0(%rip), %ebx
    movl    $.LC1, %esi
    subq    $16, %rsp
    movl    %ebx, %edx
    movl    $1, %edi
    movq    $.LC0, 8(%rsp)
    xorl    %eax, %eax
    call    __printf_chk

    movd    %ebx, %xmm0
    movl    $.LC1, %esi
    movl    $1, %edi
    movl    $1, %eax
    unpcklps    %xmm0, %xmm0
    cvtps2pd    %xmm0, %xmm0 # this is where your value went
    call    __printf_chk     # is NOT gonna read from xmm0!

    addq    $16, %rsp
    xorl    %eax, %eax
    popq    %rbx
    ret
Run Code Online (Sandbox Code Playgroud)

与clang 2.9相同的故事

    ...
    movl    $.L.str, %ebx
    xorb    %al, %al
    movl    $.L.str1, %edi     # .L.str1 is your format "%s\n"
    movl    $.L.str, %esi      # .L.str  is your static "string"
    callq   printf

    movd    %ebx, %xmm0        # your value is in xmm0 again
    cvtss2sd    %xmm0, %xmm0   # promoted to double, but still in xmm0
    movb    $1, %al
    movl    $.L.str1, %edi
    callq   printf             # printf has no idea
Run Code Online (Sandbox Code Playgroud)