Разница между интерпретацией в C ++ и приведением в стиле c

Код:

char keyStr[50]={ 0x5F, 0x80 /* bla bla */ };
uint32_t* reCast  = reinterpret_cast< uint32_t* >( &keyStr[29] );
uint32_t* reCast2 = ( uint32_t* )&keyStr[29];
if( reCast == reCast2 ){
cout << "Same Thing!";
}

Выход:

То же самое!

Интересно, в чем разница между двумя методами литья.
Также, если бы вы могли указать (с примерами) разницу между static_cast, dynamic_cast и другими типами приведения, которые вы знаете (то есть, оставаясь на низком уровне и как можно ближе к ассемблеру).

static_cast
dynamic_cast
const_cast
reinterpret_cast
C-style cast (type)value
Function-style cast type(value)

Благодарю.

Пожалуйста, прочитайте P.S.
Из приведенного выше примера я знаю, что reinterpret_cast присваивает указателю int адрес keyStr [29] В сборке это будет означать:

lea eax, [keyStr+1D]
mov [reCast], eax

Таким образом, другими словами, reinterpret_cast, в перспективе низкого уровня, совсем не опасен, поскольку не изменяет исходные данные.

Я хотел знать, как другие методы приведения ведут себя на низком уровне.
Так, например, низкоуровневый объект — это просто переменная, которая содержит адрес.
И тип, если этот объект — то, как компилятор затем интерпретирует этот адрес и как он смещает его (это как раз то, что меня не интересует, в сборке, мне было бы наплевать, если эта переменная содержит значение, указатель или объект (т.е. другой указатель)).
Другая вещь, которая может быть точно такой же, это разница между int и int * или unsigned int и int; все 4 объявления генерируют одну и ту же инструкцию по сборке. (push-значение) или (sub esp- (длина целого) && mov esp, value)
Я надеюсь, что это проясняет вопрос и почему я пометил его как «низкоуровневый код» и «сборка»

Постскриптум В этой программе, которую я пытаюсь создать, меня не волнует непереносимость или другие вещи высокого уровня. Я стараюсь быть как можно ниже и как можно ближе к языку ассемблера. Это означает, что для этой программы память — это просто память (т. Е. 0 и 1 бит), а типы не важны (например, мне все равно, если адрес mem: 0x123 — это тип «int» или тип «float», это просто «данные»)

3

Решение

reinterpret_cast а также const_cast способы обойти систему типов C ++. Как вы отметили для reinterpret_cast, это обычно приводит к небольшому или отсутствию ассемблерного кода.

static_cast в основном уважает систему типов C ++. Он может преобразовать число из одного типа в другой, вызвать конструктор или вызвать функцию преобразования. Или для преобразования из производного в основание это может включать добавление байтовых смещений и / или поисков в vtable. static_cast также может изменять правила системы типов, «понижая» указатель или ссылку с не виртуального базового типа на производный тип, возможно вычитая смещение байта.

И тогда есть указатели на член. Они, вероятно, не в этом дело, но static_cast делает вещи с ними более или менее аналогично преобразованию указателя класса.

dynamic_cast уважает систему типов C ++ еще более строго. В своей полезной форме он проверяет во время выполнения, действительно ли указатель / ссылка указывает / ссылается на объект указанного типа. Обычно он вызывает магическую библиотечную функцию под одеялом.

Приведение в стиле функции с одним аргументом имеет тот же эффект, что и приведение в стиле C. (При наличии более одного аргумента вызов в стиле функции должен быть вызовом конструктора класса.) Приведение в стиле C делает первое, что имеет смысл из следующего списка:

  • const_cast
  • static_cast
  • static_cast а затем const_cast
  • reinterpret_cast, или же
  • reinterpret_cast а затем const_cast

Единственное исключение: приведение в стиле C может игнорировать частные и защищенные отношения наследования между классами, делая вид, что вместо этого они имеют отношение открытого наследования.

Приведение в стиле C обычно не является предпочтительным в C ++, потому что оно менее конкретно о том, что вы хотите, чтобы произошло.

4

Другие решения

Каким образом вы имеете в виду «не опасно»? reinterpret_cast невероятно опасен. Он сообщает компилятору, что можно игнорировать то, что он думает о значении.

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

Понимать эти операции на ассемблере немного бессмысленно. Они не являются языками ассемблера. Это языковые конструкции C ++, которые работают следующим образом:

static_cast — Фактически это преобразует объект из одного типа в другой. Обратите внимание, что это может изменить значение (static_cast<float>(1) например, не имеет такой же битовой комбинации, как 1).

dynamic_cast — если этот объект можно считать другим типом посредством наследования, то обрабатывайте его как таковой, в противном случае визуализируйте его как ноль. Это не изменит значение указателя, но безопасно изменит его представление компилятора.

const_cast — выбросить const (или же volatile) квалификаторы, что не всегда является хорошей идеей, поскольку позволяет уничтожать данные, которые клиент считал безопасными.

reinterpret_cast — трактовать битовый паттерн как нечто отличное от того, что, по мнению компилятора, он делал. Обычно используется для указателей и, надеюсь, редко. reinterpret_castвставляя int в float вряд ли будет хорошей идеей, но она сохранит тот же шаблон битов.

c-style-cast — возьмите битовый паттерн, полностью забудьте все, что вы о нем знаете, и относитесь к нему как к чему-то еще. Опасная и почти невидимая комбинация static_cast, reinterpret_cast а также const_cast, Это не считается хорошей идеей в коде C ++, потому что это трудно заметить в обзоре, и потому что оно не является конкретным в отношении того, что происходит.

0

В вашем примере нет различий между приведением стиля C и reinterpret_cast, потому что вы выполняете приведение между несвязанными указателями, и нет константность. Если у вас был const с одной стороны, reinterpret_cast задохнулся бы, когда приведение стиля C сделало бы const_cast под капотом.

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

На низком уровне все приведения имеют одинаковый эффект (если они действительны): все они дадут значение или адрес. Основным отличием является:

  • приведение в стиле C будет (почти) всегда разрешено во время компиляции — я не знаю примеров, когда это даст ошибку компиляции, но это может зависеть от компилятора
  • reinterpret_cast будет разрешен в тех же случаях, если нет изменений константности
  • const_cast может только изменить константу
  • static_cast будет разрешен (во время компиляции) только между связанными типами

Все эти приведения были добавлены в C ++, чтобы избежать режима «перехватить все» приведения в стиле C и обеспечить некоторые проверки во время компиляции и выполнения.
— dynamic_cast будет разрешен только во время компиляции между связанными типами а также компилятор вставит код для контроля достоверности во время выполнения

0

Разница в том, что в некоторых случаях при использовании C-style cast в файле C ++ вы получите ошибку и не сможете скомпилировать. reinterpret_cast решает такие случаи. Что-то вроде — вы говорите компилятору: «Я знаю, что это несовместимое приведение, но давайте предположим, что это нормально». C ++ гораздо более ограничен, чем C для таких вещей, как приведение типов.

-1
По вопросам рекламы [email protected]