2014-11-29 8 views
3

Если я знаю адрес объекта данных, могу ли я сохранить адрес как целое число и использовать целое число как указатель?Является ли указатель целым числом?

Например,

void main(){ 
    long a = 101010; 
    long *p = &a; 
    long b = p; 
    printf("%lld\n", *(long*)b); 
} 

Это всегда безопасно?

Комментарии: long b = p; производит предупреждение:

Initialization makes integer from pointer without a cast 

Тем не менее, программа печатает 101010.

+1

В стандарте языка такого положения нет, что указатель всегда конвертируется в целое. Это просто совпадение, так как int по какой-то причине долгое время. – AlexanderVX

+0

Этот код должен вызывать предупреждение об использовании указателя как целого без приведения. – user3629249

+0

, если вы разместили этот код в файле и скомпилировали его (с включенными предупреждениями), компилятор поставил бы неправильный код. Тогда не было бы необходимости задавать вопрос – user3629249

ответ

7

Не гарантируется стандартом, что такой приведение всегда будет работать.

Чтобы сохранить указатель в интегральном типе, используйте intptr_t (или его знатный кузен uintptr_t). Гарантируется преобразование указателей void * в такие типы и конвертирование назад, что приводит к тому же значению.

Обратите внимание, что эти типы являются необязательными.

+0

Они обычно такие же, как 'ptrdiff_t' (подписанные) и' size_t' (без знака), правильно? – technosaurus

+1

@technosaurus [Не совсем] (http://stackoverflow.com/q/1464174/1009479). Также обратите внимание, что 'ptrdiff_t' и' size_t' не являются необязательными. –

-1

Указатель - это переменная, значение которой является адресом другой переменной. Как и любая переменная или константа, вы должны объявить указатель, прежде чем сможете с ним работать. Общая форма декларации переменной указатель:

type *var-name; 

Фактический тип данных значения всех указателей, является ли целое число, с плавающей точкой, характер, или иным образом, то же самое, длинное шестнадцатеричное число, которое представляет собой память адрес. Единственное различие между указателями разных типов данных - это тип данных переменной или константы, на которые указывает указатель.

Указатели обычно имеют фиксированный размер, например. в 32-битном исполняемом файле они обычно 32-битные. Есть несколько исключений, например, в старых 16-битных окнах, когда вам нужно было различать 32-битные указатели и 16-битные ... Обычно довольно безопасно предположить, что они будут едиными в пределах данного исполняемого файла на современных настольных ОС ,

+0

Так легко сделать кастинг для типа, который гарантированно имеет тот же размер, хотя ... –

0

Чтобы быть на безопасной стороны, мы должны считать, что это не безопасно .

Размер указателя на n бит системы n бит. Например, размер указателя должен быть 8 байт в любой 64-разрядной архитектуре/компиляторе. Это гарантировано.

Однако для данных не существует такого гаранта. Его сильно зависит от компилятора. В вашем случае оба имеют одинаковый размер, поэтому он работает.


EDIT:

Для предупреждения

Initialization makes integer from pointer without a cast 

Ваш компилятор очень подходит для получения предупреждения. Проверьте тип данных. Переменная int *: не, равная int переменной. В вашем случае это работает только потому, что длины одинаковы в вашем конкретном случае/реализации.

0

Даже p ценность и b ценность имеют такой же, только p может получить доступ к a т.е. возможное, чтобы указать a, b не может указывать на a. Потому что p объявлен как указатель. b объявляется переменной.

Вы не можете получить a через b.Тогда как ваш вопрос подходит. В b, адрес a трактуется как только value а не address, из-за b переменная не указатель ..

Даже печатает 101010, эта программа не является универсальным.

1

sizeof(long) обычно определяется компилятором (подверженным базовой архитектуре HW).

sizeof(long*) подвергается размеру адресного пространства виртуальной памяти на вашей платформе.

Например, с Visual Studio компилятор для 64-разрядной операционной системы и 64-разрядных процессоров:

  • sizeof(long) == 4
  • sizeof(long*) == 8

Поэтому:

  • С long b = p, только 4 наименее значимых байта p копируются в b
  • С *(long*)b, вы потенциально пытается получить доступ к недопустимому адресу ПАМЯТЬ

В этом примере, если 4 наиболее значимые байты p равны нулю, то «никакого вреда не будет сделано». Но так как это не гарантируется, когда sizeof(long) != sizeof(long*) этот код обычно небезопасен.

В дополнение к этому, даже если sizeof(long) == sizeof(long*), вы все равно не должны использовать этот тип конверсий (указатель на целое), чтобы сохранить свой код переносимым на другие платформы.


UPDATE

Пожалуйста, обратите внимание, что printf("%lld\n", *(long*)b) также небезопасно.

Вы должны в основном использовать "%ld" для значений long и "%lld" для long long значений.

Если sizeof(long) < sizeof(long long), это может привести к нарушению доступа к памяти во время работы.

Смежные вопросы