Код:
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», это просто «данные»)
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 ++, потому что оно менее конкретно о том, что вы хотите, чтобы произошло.
Каким образом вы имеете в виду «не опасно»? 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 ++, потому что это трудно заметить в обзоре, и потому что оно не является конкретным в отношении того, что происходит.
В вашем примере нет различий между приведением стиля C и reinterpret_cast, потому что вы выполняете приведение между несвязанными указателями, и нет константность. Если у вас был const
с одной стороны, reinterpret_cast задохнулся бы, когда приведение стиля C сделало бы const_cast под капотом.
Опасность reinterpret_cast (или приведения в стиле C) заключается именно в том, что он допускает приведение между несвязанными объектами. В вашем примере, у вас есть высокий риск, что при разыменовании reCast
(или же reCast2
) вы получаете сообщение об ошибке, потому что пытаетесь получить неверное целое число.
На низком уровне все приведения имеют одинаковый эффект (если они действительны): все они дадут значение или адрес. Основным отличием является:
Все эти приведения были добавлены в C ++, чтобы избежать режима «перехватить все» приведения в стиле C и обеспечить некоторые проверки во время компиляции и выполнения.
— dynamic_cast будет разрешен только во время компиляции между связанными типами а также компилятор вставит код для контроля достоверности во время выполнения
Разница в том, что в некоторых случаях при использовании C-style cast в файле C ++ вы получите ошибку и не сможете скомпилировать. reinterpret_cast
решает такие случаи. Что-то вроде — вы говорите компилятору: «Я знаю, что это несовместимое приведение, но давайте предположим, что это нормально». C ++ гораздо более ограничен, чем C для таких вещей, как приведение типов.