Когда целочисленный <-> указатель приведен правильно? - PullRequest
77 голосов
/ 22 августа 2011

Народный фольклор говорит, что:

  • Система типов существует по причине. Целые числа и указатели являются различными типами, приведение между ними в большинстве случаев является неправильной практикой, может указывать на ошибку проектирования и ее следует избегать.

  • Даже когда выполняется такое приведение, не следует делать никаких предположений о размере целых чисел и указателей (приведение void* к int - это самый простой способ заставить код не работать на x64), и вместо этого из int следует использовать intptr_t или uintptr_t из stdint.h.

Зная это, когда на самом деле полезно выполнять такие броски?

(Примечание: наличие немного более короткого кода для цены переносимости не считается «действительно полезным».)


Один известный мне случай:

  • Некоторые многопроцессорные алгоритмы без блокировки используют тот факт, что указатель с 2-байтовым выравниванием имеет некоторую избыточность. Затем они используют младшие биты указателя, например, в качестве логических флагов. С процессором, имеющим соответствующий набор команд, это может устранить необходимость в механизме блокировки (который был бы необходим, если бы указатель и логический флаг были разделены).
    (Примечание. Эту практику можно даже безопасно выполнить в Java с помощью java.util.concurrent.atomic.AtomicMarkableReference)

Что-нибудь еще?

Ответы [ 15 ]

1 голос
/ 22 августа 2011

У меня есть одно применение для такой вещи в сетевых идентификаторах объектов.Такой идентификатор будет сочетать в себе идентификаторы машины (например, IP-адрес), идентификатор процесса и адрес объекта.Чтобы быть отправленным через сокет, часть указателя такого идентификатора должна быть помещена в достаточно широкое целое число, чтобы оно выдерживало транспорт туда и обратно.Часть указателя интерпретируется как указатель (= приведенный обратно к указателю) в контексте, где это имеет смысл (тот же компьютер, тот же процесс), на других машинах или в других процессах, которые она просто служит для различения различных объектов.

То, что нужно для этой работы, - это существование uintptr_t и uint64_t как целочисленного типа с фиксированной шириной.(Хорошо работает только на машинах, которые имеют не более 64 адресов:)

1 голос
/ 22 августа 2011

Вам может потребоваться доступ к памяти по фиксированному известному адресу, тогда ваш адрес является целым числом, и вам необходимо присвоить его указателю. Это несколько распространено во встроенных системах. И наоборот, вам может потребоваться распечатать адрес памяти и, следовательно, преобразовать его в целое число.

О, и не забывайте, что вам нужно присваивать и сравнивать указатели с NULL, который обычно является приведением указателя 0L

0 голосов
/ 24 августа 2011

Существует давняя и хорошая традиция использовать указатель на объект в качестве дескриптора типа.Например, некоторые люди используют его для реализации взаимодействия между двумя модулями C ++ с помощью плоского API в стиле C.В этом случае тип дескриптора определяется как один из целочисленных типов, и любой метод должен преобразовать указатель в целое число, прежде чем его можно будет передать другому методу, который ожидает абстрактный дескриптор без типа в качестве одного из своих параметров.Кроме того, иногда нет другого способа разорвать круговую зависимость.

0 голосов
/ 23 августа 2011

Значения указателя также могут быть полезным источником энтропии для заполнения генератора случайных чисел:

int* p = new int();
seed(intptr_t(p) ^ *p);
delete p;

Библиотека повышения UUID использует этот прием и некоторые другие.

0 голосов
/ 22 августа 2011

Я использовал такие системы, когда я пытаюсь пройти побайтово через массив. Часто указатель проходит несколько байт за раз, что вызывает проблемы, которые очень трудно диагностировать.

Например, указатели int:

int* my_pointer;

перемещение my_pointer++ приведет к продвижению на 4 байта (в стандартной 32-битной системе). Однако перемещение ((int)my_pointer)++ увеличивает его на один байт.

Это действительно единственный способ сделать это, кроме наведения указателя на (char *). ((char*)my_pointer)++

По общему признанию, (char *) - мой обычный метод, так как он имеет больше смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...