请看下面的代码
#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;
}
我试图获取节点结构的 s 成员的地址, cast 它到不少于4个字节的数据类型( 4 字节是我机器上的 pointer 的宽度),然后取消引用指针作为参数 printf .
我认为两个printfs的结果应该是 same ,但第二个显示 "(null)" .
float和int在我的机器上具有相同的字节宽度,是导致这种情况的两种类型的 internal different representation 吗?
提前致谢 !
4 回答
您的程序调用未定义的行为,因为
printf()
的参数类型不是printf所期望的 . 通过查看源代码无法预测结果 .C99-TC3,
§7.19.6.1/9
但是,如果您对行为的原因感兴趣,那么您的编译器可能是将浮点值传递给浮点CPU寄存器中的printf()的编译器之一 . (例如,GNU和CLang这样做) . 对printf的第二次调用将取消引用的值放在浮点寄存器中,但是
printf
,看到%s
转换说明符,查看了传递char*
的寄存器,可能是通用寄存器,恰好是零你的情况 .PS:这是GCC 4.6.1在我的Linux上做出的
与clang 2.9相同的故事
您的期望显然基于您认为可变函数的可变参数以某种特定方式传递给这些函数的信念 . 这已经非常依赖于实现,所以从正式的C语言来看,你的实验已经没有多大意义了 .
我猜你期望将可变参数复制到某种类型的"variadic argument array"(堆栈帧?)作为原始内存块,而不管它们的类型特定语义 . 出于这个原因,您显然认为
int
参数应该以与float
参数完全相同的方式传递,因为这两种类型碰巧在您的平台上具有相同的大小 .这种假设完全没有根据,也是不正确的 . 在这种情况下,实际传递给
printf
的是所讨论的参数的值,并且因为这些值具有完全不同的特定于类型的语义,所以它们可以以完全不同的方式轻松传递 . 不用说,代码的行为是未定义的,原因多于一个 .在这种情况下,您需要理解的一个基本事情是,完全不可能将
float
值作为可变参数函数的可变参数传递 . 根据语言规范的要求,所有float
值在传递之前会自动提升为double
值 . (这同样适用于char
和short
值,它们始终首先提升为int
. )考虑到在您的情况下float
值是通过重新解释指针对象占用的内存获得的,然后提升为double
,因此,这并不奇怪你观察到的结果没有任何意义 .你需要理解的另一个基本的事情是C语言不允许重新解释由一种类型的对象和另一种类型的对象占用的内存(在某种意义上,结果行为是未定义的) . 不允许将指针对象占用的内存重新解释为
int
对象 . 这正是你想要做的 . 即使是你所说的第一个printf
,也是偶然的 .是 . 二进制中a float和an integer的内部表示形式大不相同 .
如果需要地址,请在printf()中使用“%p”格式说明符 . 自K&R2以来,它一直在C中 .