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在我的机器上有相同的字节宽度,是导致这种情况的两种类型的内部不同表示吗?
提前致谢 !
您的程序调用未定义的行为,因为参数的类型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)