Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Что происходит при приведении типов?

#c #cpp


Если приведение в Си стиле (float)i; или float(i);, то по сути в обоих случаях вызывается
конструктор и создается временный объект? Но а что в случае с указателями, если привести
int* p к  (float*)p, то почему нельзя написать float*(p) и что делается в случае с
(float*)p?

UPD:
@VladD, За static_cast что-то не понятно. К примеру есть Си стиль: 
char* str = "123456789";
int* pi = (int*)str;

а с static_cast будет :
char* str = "123456789";
void* pv = static_cast(str);
int* pi = static_cast(pv);

так вот зачем к войду приводить, а не сразу к int?
Это промежуточное приведение сделанное потому, что в Си нету ограничений в плане
присвоения указателя одного типа указателю другого типа, а в C++ есть? То есть, получается,
что если написать в С++ (int*)str, то он будет разложен на static_cast и static_cast?
А reinterpret_cast каким образом тогда игнорирует это ограничение в C++? Или он тоже
использует static_cast?
char* str = "123456789";
int* pi = reinterpret_cast(str);

@avp, я так понял, что в Си не будет работать это :
int a;
float b = float(a);

потому что в Си нету классов и не будет вызван конструктор, а в конструкторе неявно
приведен. Но а как тогда в Си работает такое :
int a;
float b = (float)a;

ведь если в C++ при (float)a; выбирается static_cast или const_cast или reinterpret_cast,
то что в Си происходит при (float)a?    


Ответы

Ответ 1



Вообще-то, приведение типов — огромная тема, в ней сброшены в одну кучу и преобразования между арифметическими типами (расширение/сужение различных видов целого, преобразование между целым и плавающей запятой и обратно), и переход вверх/вниз по иерархии наследования через указатели, и динамический контроль типов времени выполнения, и добавление/снятие модификаторов const/volatile, всё подряд. Все правила охватить — тема для раздела с исследованиями. Продублирую ссылки на документацию: const_cast static_cast reinterpret_cast dynamic_cast C-style cast По поводу вашего вопроса о const_cast: смотрите. Вы не можете сделать static_cast из char* в int* по правилам, перечисленным тут. Самым подходящим могли бы быть правила 1 и 5, но поскольку нет неявной конверсии между char* и int* (char и int не являются подклассами друг друга), то и прямой static_cast невозможен. С другой стороны, static_cast из char* в void* возможен по правилу 1, поскольку есть неявная конверсия из любого указателя на данные в void*. Обратная конверсия из void* в int* возможна по правилу 10. Возникает закономерный вопрос, а зачем нужны такие ограничения? Дело в следующем. Прямая конверсия запрещена, потому что использовать полученный указатель иначе чем для того, чтобы преобразовать его назад к char*, вы не имеете права по стандарту. Это происходит вследствие так называемого strict aliasing rule. Лучше всего почитать подробное объяснение с указанием причин здесь, вкратце: если указатель указывает на char, вы не можете обращаться к int'у по этому адресу. [Единственное исключение — преобразование в char*.] Поэтому язык предохраняет вас от ошибки: если вы уж написали дважды static_cast через void*, вы должны понимать, что делаете что-то специальное. Напротив, C-style cast — гораздо более безалаберная штука: компилятор пробует по очереди const_cast, static_cast, static_cast + const_cast, reinterpret_cast, reinterpret_cast + const_cast, и берёт первое, что подходит. Такое поведение более типично для языков типа Visual Basic'а: вы не контролируете, что именно происходит, вы говорите компилятору «короче, реши за меня сам». C-style cast в C — единственный существующий вид явного приведения типов. Поскольку в нём нету семантики наследования, в нём все операции преобразования указателей являются по существу пустыми, и просто изменяют взгляд компилятора на данные по данному адресу. (Вообще, грубо говоря, C работает с байтами, а C++ — с абстрактными сущностями.) Тем не менее, strict alisaing rule работает и в C. Заметьте, что в C++ преобразование указателя между базовым и производным типами может приводить к изменению адреса — в отличие от C. Это означает, что reinterpret_cast может дать неправильный результат, даже если static_cast даёт правильный! То есть, нельзя бездумно использовать reinterpret_cast в надежде, что он всё сделает правильно. И это также объясняет, почему C-style cast не сводится к reinterpret_cast.

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

Отправить комментарий