Почему мой static_cast указателя терпит неудачу?

#c #visual-c

#c #visual-c

Вопрос:

Почему мой static_cast указателя терпит неудачу?

 int iDog = 456;
int *piDog = amp;iDog;
long *plDog = static_cast<long*>(piDog); // invalid type conversion

long lDog = static_cast<long>(iDog);    // ok

long* plDog = (long*)(piDog); // this is OK too... very weird!! (Dynamic cast... and we aren't allowed to use this one in our coding standards)
 

Эта ссылка предполагает, что все должно быть в порядке: https://en.cppreference.com/w/cpp/language/static_cast

Проблема с Visual Studio C ?

Комментарии:

1. Подсказка: sizeof(long) != sizeof(int) . piDog недостаточно велик, чтобы вместить long .

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

3. Как эта ссылка предполагает, что все в порядке? Удивительно, что вы думаете, что Visual Studio неправильно поняла бы такой тривиальный фрагмент кода. Используйте reinterpret_cast вместо этого.

4. Ссылка, на которую вы ссылаетесь, содержит 10 точек, указывающих разрешенные приведения. Это оставляет читателю много догадок. Возможно, вы могли бы отредактировать свой вопрос и добавить объяснение того, какой пункт, по вашему мнению, применим и почему он применим?

5. (long*) это кастинг Молотом Божьим. Независимо от того, насколько плохим, глупым или безумным является преобразование, приведение в стиле C сделает это возможным. Мое эмпирическое правило, когда я вижу одного из этих детей, — более внимательно изучить код на предмет ошибки, которую он скрывает.

Ответ №1:

 long *plDog = static_cast<long*>(piDog); // invalid type conversion
 

Почему мой static_cast указателя терпит неудачу?

Потому что он неправильно сформирован. Ни одно из правил static_cast не применяется к приведению, которое вы пытаетесь выполнить. Это недопустимое преобразование, как вы упоминаете в комментариях.

Указатель на один тип объекта не может быть статически преобразован в указатель на другой тип объекта, если они не являются указателями на связанные классы или при приведении к / из указателя на void .

Эта ссылка предполагает, что все должно быть в порядке: https://en.cppreference.com/w/cpp/language/static_cast

Эта ссылка предполагает, что преобразование, которое вы пытаетесь выполнить, не в порядке.

 long* plDog = (long*)(piDog); // this is OK too... very weird!!
 

Это правильно сформированное преобразование. Это не «странно».

Существует много преобразований, которые разрешают явные преобразования (также называемые «приведение нотации» или «приведение в стиле C»), а статические приведения не разрешают. Это потому, что статические приведения имеют (по крайней мере, видимость) безопасности типов, в то время как явные преобразования по существу просят компилятор притвориться, что система типов не существует.

Обратите внимание, что косвенное plDog обращение к объекту и доступ к нему приведут к неопределенному поведению. Как вы можете видеть, хорошо, что вы получили сообщение об ошибке.

и нам не разрешено использовать это в наших стандартах кодирования

Это хорошее ограничение. Вашей команде будет немного сложнее писать ошибки, ошибочно обходя систему типов.

Проблема с Visual Studio C ?

Нет, проблема в том, что программа плохо сформирована. Компилятор правильно информирует вас об ошибке и не требуется для компиляции программы.


Я рекомендую спросить себя: почему вы хотите или думаете, что вам нужно выполнить такое приведение?

Ответ №2:

TL; DR: Если бы ваше приведение работало, язык не давал бы никаких гарантий безопасности типов для указателей, и это ключевая часть мотивации для введения приведений в стиле C вместо того, чтобы придерживаться приведения старой школы C.

На языке стандарта C / C long* и int* не являются «совместимыми с указателями». Вы не можете неявно преобразовать a long* в an int* или наоборот, поэтому static_cast не можете сделать это за один раз.

Причина этого sizeof(long) не всегда одинакова sizeof(int) для всех платформ. Это разные фундаментальные типы. Это верно в целом для всех различных типов C / C , даже если они имеют идентичную двоичную компоновку. Они только «совместимы с указателями», если в синтаксисе языка вы объявляете типы, которые должны быть связаны с помощью неявного преобразования.

Вы можете использовать static_cast при преобразовании из a void* в любой указатель на тип объекта или наоборот.

Поэтому вы можете сделать это двумя способами:

  • reinterpret_cast<long*>(piDog);

-или-

  • static_cast<long*>(static_cast<void*>(piDog));

Стилистически использование reinterpret_cast намного понятнее. В любом случае достоверность приведения зависит от архитектуры и предполагает sizeof(int) == sizeof(long) , а также одинаковое расположение памяти.

IOW Это безопасно для Windows x86 и x64 native, но может быть недоступно для других платформ или процессоров. Это одна из причин, по которой Windows x64 предпочитает использовать модель данных LLP64, как описано в этом сообщении в блоге.

Смотрите cppreference для static_cast и reinterpret_cast

Комментарии:

1. Имеют ли они одинаковый размер или нет int , long не имеет значения; типы не связаны (один не является производным от другого), и a static_cast не допускается между указателями на несвязанные типы.

2. Уверен, что они не являются «совместимыми с указателями» на языке стандарта. Я пытался обратиться к комментарию OP «Но они оба типа указателей и (обычно) sizeof(long *) == sizeof(int *) «.

3. Первый ответ был немного грубым, поэтому я попытался его немного почистить…

4.Meh. Я думаю, вы упустили момент, который заключается в том, что OP пытается использовать указатели, а не простые int и long типы данных. Вы могли typedef бы использовать два идентичных struct типа, и все равно расстояние static_cast между их указателями было бы недействительным