开发者

weird difference between int pointer and float pointer

开发者 https://www.devze.com 2023-04-10 01:36 出处:网络
please see my codes below #include <stdio.h> #include <stddef.h> typedef struct _node { int a;

please see my codes below

#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;
} 

i attempt to get the address of the s member of the Node structure, cast it to a data types not less than 4 bytes(4 byte is the width of a point开发者_开发百科er on my machine), then de-reference the pointer as a argument to printf.

i do think the outcome of two printfs should be the same, but the second one displays "(null)" .

float and int have the same byte width on my machine, is the internal different representation of the two types that cause this ?

thanks in advance !


Your program invokes undefined behavior because the types of the arguments to printf() are not what printf expects. There is no way to predict the outcome by looking at the source code.

C99-TC3, §7.19.6.1/9

If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

However, if you are interested in the reason the behavior you've observed is as it is, it's likely that your compiler is one of those that pass floating-point values to printf() in the floating-point CPU registers. (GNU and CLang do that, for example). The second call to printf placed the dereferenced value in a floating-point register, but printf, seeing the %s conversion specifier, looked at the register where a char* would have been passed, likely a general-purpose register, which happened to be zero in your case.

PS: Here's what GCC 4.6.1 makes out of it on my 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

Same story with 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


Your expectations are apparently based on your belief that variadic arguments of variadic functions are passed to those functions in some specific way. This is already very implementation-dependent, so from the point of the formal C language your experiments already make very little sense.

I'd guess that you expect the variadic arguments to be copied to the "variadic argument array" of some sort (stack frame?) as blocks of raw memory, regardless of their type-specific semantics. For this reason you apparently believe that an int argument should be passed in exactly the same way as a float argument since both types happen to have the same size on your platform.

This assumption is totally unfounded and incorrect. What is actually passed to printf in this case is the values of the arguments in question, and since these values have completely different type-specific semantics, they can easily be passed in completely different ways. Needless to say, the behavior of your code is undefined for more reasons than one.

One basic thing that you need to understand in this case is that it is completly impossible to pass a float value as a variadic parameter of a variadic function. All float values are automatically promoted to double values before passing, as required by the language specification. (The same applies to char and short values, which are always promoted to int first.) Considering that in your case the float value was obtained by reinterpreting memory occupied by a pointer object, and then promoted to double, it is not surprising that the results you observe make no sense whatsoever.

Another basic thing that you need to understand that reinterpreting memory occupied by an object of one type and an object of another type is no allowed by C language (in a sense that the resultant behavior is undefined). You are not allowed to reinterpret memory occupied by a pointer object as an int object. And this is exactly what you are attempting to do. Even the first of your printfs, which allegedly "works as expected", does so only by accident.


Yes. The internal representation of a float and an integer in binary is vastly different.


If you want the address, use the "%p" format specifier with printf(). It's been in C since K&R2 and maybe before.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号