Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast from 32-bit address to 64-bit integer yields unexpected results [duplicate]

Given the following program:

#include <stdio.h>

int main(int argc, char** argv)
{
    int i;
    void* p = &i;

    printf("No cast, using %%p: %p\n",
        p);
    printf("Cast to unsigned long using conversion %%lx: %lx\n",
        (unsigned long) p);
    printf("Cast to unsigned long long using conversion %%llx: %llx\n",
        (unsigned long long) p);
    printf("Cast to unsigned long then unsigned long long using conversion %%llx: %llx\n",
        (unsigned long long) (unsigned long) p);

    return 0;
}

What would one expect the output to be? Compiling with GCC 4.4.7 and running the program given the following output:

No cast, using %p: 0xbf8aa3d8
Cast to unsigned long using conversion %lx: bf8aa3d8
Cast to unsigned long long using conversion %llx: ffffffffbf8aa3d8
Cast to unsigned long then unsigned long long using conversion %llx: bf8aa3d8

Compiling with clang version 3.4 and running the program yields what I would actually expect:

No cast, using %p: 0xbfa64234
Cast to unsigned long using conversion %lx: bfa64234
Cast to unsigned long long using conversion %llx: bfa64234
Cast to unsigned long then unsigned long long using conversion %llx: bfa64234

It would appear that GCC has padded the most significant bits with 1's instead of 0's when converting directly from pointer to unsigned long long. Is this a bug in GCC?

like image 328
dlp Avatar asked Jul 30 '14 14:07

dlp


1 Answers

This is implementation defined as per the draft C99 standard section 6.3.2.3 Pointers which says:

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

gcc documents there implementation here:

The result of converting a pointer to an integer or vice versa (C90 6.3.4, C99 and C11 6.3.2.3).

  • A cast from pointer to integer discards most-significant bits if the pointer representation is larger than the integer type, sign-extends1 if the pointer representation is smaller than the integer type, otherwise the bits are unchanged.

  • A cast from integer to pointer discards most-significant bits if the pointer representation is smaller than the integer type, extends according to the signedness of the integer type if the pointer representation is larger than the integer type, otherwise the bits are unchanged.

Alternatively you could use uinitptr_t assuming stdint.h is available which is an unsigned integer that can hold a pointer value:

#include <stdint.h>
#include <inttypes.h>

uintptr_t ip = &i ;

printf("0x%016" PRIxPTR "\n", ip ) ;
like image 142
Shafik Yaghmour Avatar answered Nov 01 '22 04:11

Shafik Yaghmour