Приведение от 32-разрядного адреса к 64-разрядному целому дает неожиданные результаты [дубликат]

этот вопрос уже есть ответ здесь:

учитывая следующую программу:

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

что можно было бы ожидать от выхода? Компиляция с GCC 4.4.7 и запуск программы с учетом следующего вывод:

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

компиляция с clang версии 3.4 и запуск программы дает то, что я действительно ожидал бы:

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

похоже, что GCC добавил наиболее значимые биты с 1 вместо 0 при преобразовании непосредственно из указателя в беззнаковый длинный длинный. Это ошибка в GCC?

1 ответов


это реализация, определенная в соответствии с проект стандарта C99 раздел 6.3.2.3 указатели он говорит:

любой тип указателя может быть преобразован в целочисленный тип. Кроме как уже указано, результат определяется реализацией. Если результат не может быть представлен в виде целого числа , поведение не определено. Результат не должен находиться в диапазоне значений любого целого числа тип.

gcc документы там реализация здесь:

результат преобразования указателя в целое число или наоборот (C90 6.3.4, C99 и C11 6.3.2.3).

  • приведение от указателя к целому числу отбрасывает наиболее значимые биты, если представление указателя больше, чем integer type, sign-extends1, если представление указателя меньше чем целочисленный тип, в противном случае биты не изменяются.

  • литой от integer к pointer отбрасывает наиболее значимые биты, если представление указателя меньше размера целого типа, расширяет согласно signedness типа integer если указатель представление больше, чем целочисленный тип, в противном случае биты не менявшийся.

в качестве альтернативы вы можете использовать uinitptr_t предполагая, что stdint.h доступно целое число без знака, которое может содержать значение указателя:

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

uintptr_t ip = &i ;

printf("0x%016" PRIxPTR "\n", ip ) ;