правильный (или самый безопасный) способ инициализации пустого указателя с ненулевым значением? - PullRequest
5 голосов
/ 23 октября 2019

Следующие работы:

void* p = reinterpret_cast<void*>(0x1000);

Но выглядит «неправильно / небезопасно», например. 0x1000 - это int и даже не uintptr_t, я мог бы это исправить, но есть ли лучший / более безопасный метод приведения?

Ответы [ 3 ]

4 голосов
/ 23 октября 2019

0x1000 - это int и даже не uintptr_t, я мог бы это исправить, но есть ли лучший / более безопасный метод приведения

0x1000 - это intи в reinterpret_cast<void*>(0x1000) компилятор выдает команду расширения знака (здесь знак равен 0) или простую инструкцию загрузки регистра с непосредственным операндом, чтобы сделать значение таким широким, как void*.

По многим причинам компилятор не может знать, представляет ли 0x1000 действительный адрес, поэтому он должен соответствовать и предполагать, что это действительный адрес.

Приведение целых чисел, представляющих адреса к указателям сreinterpret_cast В настоящее время существует практика.

1 голос
/ 23 октября 2019

Вы можете создать указатель из целого числа с reinterpret_cast.

Однако указатель, который не указывает на существующий объект (или 1 после последнего элемента в массиве с объектом, который считаетсяединственный элемент в воображаемом массиве для этой цели (или нулевой указатель) имеет недопустимое значение указателя. Разыменование - это UB, а другие операции с недопустимыми значениями указателя зависят от реализации, поэтому вам необходимо убедиться, что ваш компилятор разрешает операции, которые вы делаете с этими указателями.

void* p = reinerpret_cast<void*>(0x1000); // invalid pointer,
                                          // operations on it are implementation defined

§6.7.2 Составтипы [basic.compound]

[...] Каждое значение типа указателя является одним из следующих:

3.1 - указатель на объект или функцию (указатель, как говорят, указывает на объект или функцию)или

3.2 - указатель за концом объекта (8.5.6), или

3.3 - значение нулевого указателя (7.11) для этого типа, или

3.4 - недопустимое значение указателя

§8.5.1.10 Переинтерпретация приведения [expr.reinterpret.cast]

Указатель может быть явно преобразован в любой целочисленный тип, достаточно большой для его хранения. Функция отображения определяется реализацией. [...] Значение целочисленного типа или типа перечисления может быть явно преобразовано в указатель. Указатель, преобразованный в целое число достаточного размера (если таковое существует в реализации) и обратно в тот же тип указателя, будет иметь свое первоначальное значение;в противном случае сопоставления между указателями и целыми числами определяются реализацией.

Вы можете преобразовать целое число в указатель, но если полученное значение указателя не указывает на существующий объект (или одинпосле объекта) результирующий указатель имеет недопустимое значение.

Теперь о том, что вы можете делать с недопустимыми указателями:

§6.6.4 Срок хранения [basic.stc]

[...] Переадресация через недопустимое значение указателя и передача недопустимого значения указателя в функцию освобождения имеют неопределенное поведение. Любое другое использование недопустимого значения указателя имеет поведение, определяемое реализацией 35

35) Некоторые реализации могут определять, что копирование неверного значения указателя вызывает системную ошибку времени выполнения.


Это сообщение было сильно отредактировано, потому что оно было неверным в его первых итерациях. Огромное спасибо сообществу за исправление и вызов мне.

0 голосов
/ 23 октября 2019

tl; dr: Вы, вероятно, просто не должны этого делать.

Следующее ... выглядит "неверным / небезопасным" ... есть ли лучший / более безопасный метод приведения?

Как уже указывалось в другом ответе, это "неправильно" в той степени, в которой поведение, определяемое реализацией. Кроме того, вы используете магическое число.

Но, я думаю, проблема не в кастинге. Я действительно сомневаюсь, что вам нужно инициализировать переменную void* с буквальным адресом. Почему?

  • Если по этому адресу есть какое-то типизированное значение, то не используйте void *, а скорее типизированный указатель. Даже если вы позже захотите передать этот указатель на memset() или memcpy(), или даже на какую-нибудь функцию обратного вызова, которая принимает void * - задержите удаление типа.
  • Откуда вы взяли этот номер? Конечно, вы знаете, магические числа плохие , верно? Ну, по крайней мере, используйте что-то вроде

    constexpr const uintptr_t sound_card_memory_mapped_buffer_address {0x1000};

    , это решает одну из ваших проблем (не uintptr_t), а также понятнее для чтения, даже если вы остаетесь с void *:

    void* p = reinterpret_cast<void*>(sound_card_memory_mapped_buffer_address);
    
  • p - плохой выбор имени. Переименуйте его в соответствии с его использованием.

  • Вам действительно нужно инициализировать p вообще?

    • Если вы не используете его в данный моментзачем вообще его инициализировать? Попробуйте просто не объявлять его, пока он не будет использован (если вообще).
    • Если вы собираетесь его использовать, зачем даже объявлять это? Попробуйте:

      do_something_with(sound_card_memory_mapped_buffer_address);
      

      и не p в поле зрения.

Очевидно, у меня больше вопросов, чем ответов для вас, поскольку вы толькопредоставил нам однострочник.

...