Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.57/21: Рейтинг темы: голосов - 21, средняя оценка - 4.57
0 / 0 / 0
Регистрация: 26.06.2016
Сообщений: 65
1

Приведение указателя базового типа

14.08.2016, 12:00. Показов 4001. Ответов 23

Author24 — интернет-сервис помощи студентам
У меня не получается выполнить приведение указателя базового типа. Я, и static_cast пробовал, и dynamic_cast пробовал, но компилятор всё равно выводит сообщение, что ptra - это не указатель.
C++
1
2
3
4
5
6
7
8
9
#include <iostream>
int main(int argc, char **argv)
    {
        short a = 96;
        short *ptra = &a;
        int b = static_cast<int>(a);
        int *ptrb = static_cast<int>(ptra);
        return 0;
    }
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
14.08.2016, 12:00
Ответы с готовыми решениями:

Приведение типа указателя
Здравствуйте, у меня такой вопрос: как организовать приведение типа указателя к другому? Есть...

Приведение типа указателя на метод
Здравствуйте. Есть функция, принимающая void (*f)(), возможно ли использовать ее в классе,...

Создание указателя типа базового класса на экземпляр производного класса
Добрый день! Иногда видел коды, где создавался указатель типа базового класса на объект класса -...

Вызов виртуального метода базового класса из указателя производного
Допустим есть такой код: #include &lt;iostream&gt; class Base { public: virtual void f() {...

23
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
14.08.2016, 12:08 2
kokzahvas, а где здесь базовый тип? Что Вы вообще хотите сделать? Сейчас этот код чуть страшнее бреда
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
14.08.2016, 12:15 3
int* ptrb = reinterpret_cast<int*>(ptra);
0
806 / 533 / 158
Регистрация: 27.01.2015
Сообщений: 3,017
Записей в блоге: 1
14.08.2016, 12:15 4
kokzahvas, для указателей нада использавать reinterpret cast
0
0 / 0 / 0
Регистрация: 26.06.2016
Сообщений: 65
15.08.2016, 07:18  [ТС] 5
Цитата Сообщение от HelicopterK52 Посмотреть сообщение
kokzahvas, а где здесь базовый тип? Что Вы вообще хотите сделать? Сейчас этот код чуть страшнее бреда
Возможно я скажу очень смешно, но я считал, что char, short, int, long, long long, float и double - это всё базовые элементарные типы данных.

Добавлено через 4 минуты
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
kokzahvas, для указателей нада использавать reinterpret cast
А я читал что reinterpret_cast производит небезопасное приведение типа и его опасно использовать?

Добавлено через 49 минут
Я исправил код, но теперь у меня не выводится значение переменной a через приведённый указатель ptrb. Строка 13 делает неправильный вывод.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
int main(int argc, char **argv)
    {
        short a = 96;
        short *ptra = &a;
        int b = static_cast<int>(a);
        int *ptrb = reinterpret_cast<int*>(ptra);
        std::cout << "Значение переменной a: " << a << std::endl;
        std::cout << "Значение переменной b: " << b << std::endl;
        std::cout << "Адрес переменной a через указатель ptra: " << ptra << std::endl;
        std::cout << "Адрес переменной a через указатель ptrb: " << ptrb << std::endl;
        std::cout << "Значение переменной a через указатель ptra: " << *ptra << std::endl;
        std::cout << "Значение переменной a через указатель ptrb: " << *ptrb << std::endl;
        return 0;
    }
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
15.08.2016, 08:22 6
Цитата Сообщение от kokzahvas Посмотреть сообщение
теперь у меня не выводится значение переменной a через приведённый указатель ptrb.
Это ожидаемо. В этом как раз и заключается "небезопасность" reinterpret_cast - ответственность за его использование целиком ложится на программиста. В данном случае short меньше, чем int, поэтому пытаясь интерпретировать память, где лежит значение short, как значение int, мы получаем чтение еще части чужих данных, которые вместе с числом 96 и составляют этот самый "неправильный" вывод.

Чтобы лучше это понять, возьми тетрадный лист, обведи там две клетки. Представь, что указатель &a указывает на первую из них. Когда ты приводишь такой указатель к int, то при попытке вывода значения будет чтиано еще две клетки за двумя обведенными. Понимаешь?
0
Падаван С++
447 / 261 / 89
Регистрация: 11.11.2014
Сообщений: 916
15.08.2016, 08:31 7
DrOffset, тогда вопрос, как с таким бороться или приходится кастовать к типам которые имеют такой же размер в байтах как и тот из которого мы приводим ? Или еще вариант если знааем на сколько бит больше тип к котором мы приводим, после "превращения" занулить лишние биты ?
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
15.08.2016, 09:15 8
Цитата Сообщение от obivan Посмотреть сообщение
тогда вопрос, как с таким бороться
Как правило, так кастовать не нужно вообще. Если так получается, что нужен каст, то возможно что-то неправильно на более высоком уровне.
Рассматривать целесообразность можно на конкретной задаче. А для общего случая я могу сказать только "не делай так".

Добавлено через 32 минуты
obivan, что касается мер защиты, то в отдельных случаях (не обязательно связанных с кастами), можно предусмотреть дополнительные проверки времени компиляции. Например вот такой код:
C++
1
2
3
4
5
template <typename D, typename S>
typename std::enable_if<(sizeof(D) <= sizeof(S)), D *>::type safe_reinterpret_cast(S * r)
{
    return reinterpret_cast<D *>(r);
}
защитит от возможности скастить указатель на меньший тип, к большему. Ну и можно использовать static_assert, чтобы получить более читаемое сообщение об ошибке.

Если нужно составить int из двух short, то каст к int * не нужен (в том числе из-за возможных проблем с выравниванием). Относительно безопасный способ - использовать memcpy (она на многих процессорах разворачивается в одну инструкцию), а еще безопаснее - битовые сдвиги.
Например вот так:
C++
1
2
3
4
int16_t one = 1;
int16_t two = 2;
 
int32_t dest = ((two << sizeof(two)) | one);
Вариант с memcpy будет выглядеть так:
C++
1
2
3
4
5
6
int16_t one = 1;
int16_t two = 2;
 
int32_t dest;
std::memcpy(&dest, &one, sizeof(one));
std::memcpy(reinterpret_cast<char *>(&dest) + sizeof(one), &two, sizeof(two));
У этого кода тоже есть потенциальная проблема, связанная с порядком байт - на разных платформах, можем получить разный результат. Это в общем-то еще раз доказывает, что необходимость в таком должна быть оправдана и все возможные нюансы программистом предусмотрены.
1
Падаван С++
447 / 261 / 89
Регистрация: 11.11.2014
Сообщений: 916
15.08.2016, 09:30 9
Цитата Сообщение от DrOffset Посмотреть сообщение
C++
1
2
3
4
5
template <typename D, typename S>
typename std::enable_if<(sizeof(D) <= sizeof(S)), D *>::type safe_reinterpret_cast(S * r)
{
* * return reinterpret_cast<D *>(r);
}
вопрос по коду, просто чтобы уточнить, мы тут принимаем 2 типа и данная функция кастования будет доступна только в случае если тот тип к которому мы кастуем будет больше чем от которого происходит каст ?

P.S. очень понравился вариант со сдвигом
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
15.08.2016, 09:43 10
Цитата Сообщение от obivan Посмотреть сообщение
данная функция кастования будет доступна только в случае если тот тип к которому мы кастуем будет больше чем от которого происходит каст ?
Цитата Сообщение от DrOffset Посмотреть сообщение
C++
1
2
3
std::enable_if<(sizeof(D) <= sizeof(S))
...
return reinterpret_cast<D *>(r);
obivan, Вы серьёзно?

Добавлено через 3 минуты
Если боязно использовать reinterpret_cast для побитового приведения базовых типов, то есть union:
C++
1
2
3
4
5
union ShortAndInt
{
    short s;
    int i;
}
reinterpret_cast - лучше использовать для приведения типов указателей (как правило из void* в указатель на класс), не связанных иерархией наследования.
0
Падаван С++
447 / 261 / 89
Регистрация: 11.11.2014
Сообщений: 916
15.08.2016, 09:50 11
Цитата Сообщение от Operok Посмотреть сообщение
obivan, Вы серьёзно?
да ну мне лично кусок кода верхний не совсем понятен, поэтому и спросил
0
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
15.08.2016, 09:51 12
Цитата Сообщение от DrOffset Посмотреть сообщение
защитит от возможности скастить указатель на меньший тип, к большему.
Может лучше static_assert?
C++
1
2
3
4
5
6
template <typename D, typename S>
D *safe_reinterpret_cast(S *r)
{
    static_assert(sizeof(D)<=sizeof(S), "sizeof(Destination Type) <= sizeof(Source Type)");
    return reinterpret_cast<D *>(r);
}
Хоть сообщение можно выдать и без дебрей SFINAE
0
Падаван С++
447 / 261 / 89
Регистрация: 11.11.2014
Сообщений: 916
15.08.2016, 09:51 13
Operok, P.S все, я сейчас более трезво персмотрел код все ок
0
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
15.08.2016, 09:51 14
Цитата Сообщение от Operok Посмотреть сообщение
то есть union
В union в один момент должно быть активно только одно из "полей", иначе UB.
0
DrOffset
15.08.2016, 09:52
  #15

Не по теме:

Цитата Сообщение от HelicopterK52 Посмотреть сообщение
Может лучше static_assert?
Я ж про это написал выше.

0
HelicopterK52
15.08.2016, 09:54
  #16

Не по теме:

DrOffset, видимо, я мало спал :wall: Ну ладно, хоть пример привел, малая, да польза от сообщения всё же есть :D

0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
15.08.2016, 10:05 17
Цитата Сообщение от HelicopterK52 Посмотреть сообщение
В union в один момент должно быть активно только одно из "полей", иначе UB.
Что значит "активно"? Приведите пример UB.
C++
1
2
3
4
5
6
7
8
/*это WinAPi, но всё же*/
SYSTENTIME st; //структура времени Windows
LARGE_INTEGER lint;  //union
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
lint.LowPart = ft.dwLowDateTime;
lint.HighPart = ft.dwHighDateTime;
int64_t dtime = lint.QuadPart;  //сохранили метку времени в 8-байтном целом
Если в поле большего размера попадает неинициализированный мусор, то "сам дурак".
0
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
15.08.2016, 10:13 18
Цитата Сообщение от Operok Посмотреть сообщение
Приведите пример UB.
Фактически, Вы сами его привели
Цитата Сообщение от Operok Посмотреть сообщение
union
Для которого есть явное указание:
The LARGE_INTEGER structure is actually a union. If your compiler has built-in support for 64-bit integers, use the QuadPart member to store the 64-bit integer. Otherwise, use the LowPart and HighPart members to store the 64-bit integer.
Здесь не сказано о всяких "преобразованиях".
Плюс ко всему, WinAPI рассчитан на C, со всеми вытекающими.
http://stackoverflow.com/quest... d-behavior
http://stackoverflow.com/quest... in-c-and-c
0
183 / 181 / 66
Регистрация: 15.02.2015
Сообщений: 515
15.08.2016, 10:26 19
Простой способ разбить тип REAL (стандартное 4-байтное число с плавающей точкой) в массив байт с "тупоконечным" (4-3-2-1) порядком байт (например по модбасу отправить):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef float REAL;
static_assert(sizeof REAL != 4, "Wrong size REAL type");
union RealToBytes
{
    REAL r;
    uint8_t bytes[4];
}
...
RealToBytes rtb;
rtb.r = some_real_value;
uint8_t result[4];
result[3] = rtb.bytes[0];
result[2] = rtb.bytes[1];
result[1] = rtb.bytes[2];
result[0] = rtb.bytes[3];
Добавлено через 10 минут
Цитата Сообщение от HelicopterK52 Посмотреть сообщение
Для которого есть явное указание:
т.е. в данном случае UB возможно если платформа не поддерживает 64-битное целое? Это не UB, так как в таком случае оно даже не скомпилится.
0
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
15.08.2016, 11:06 20
Цитата Сообщение от Operok Посмотреть сообщение
т.е. в данном случае UB возможно если платформа не поддерживает 64-битное целое?
Запись в одно поле и чтение из другого - вот это само по себе UB.
Пройдите по ссылкам Выше, там достаточно на эту тему написано.

Добавлено через 36 минут
Пост (прост он мне показался самым легким) из одной из статей и самопальный перевод:
The purpose of unions is rather obvious, but for some reason people miss it quite often.

The purpose of union is to save memory by using the same memory region for storing different objects at different times. That's it.

It is like a room in a hotel. Different people live in it for non-overlapping periods of time. These people never meet, and generally don't know anything about each other. By properly managing the time-sharing of the rooms (i.e. by making sure different people don't get assigned to one room at the same time), a relatively small hotel can provide accomodations to a relatively large number of people, which is what hotels are for.

That's exactly what union does. If you know that several objects in your program hold values with non-overlapping value-lifetimes, then you can "merge" these objects into a union and thus save memory. Just like a hotel room has at most one "active" tenant at each moment of time, a union has at most one "active" member at each moment of program time. Only the "active" member can be read. By writing into other member you switch the "active" status to that other member.

For some reason, this original purpose of the union got "overriden" with something completely different: writing one member of a union and then inspecting it through another member. This kind of memory reinterpretation is not a valid use of unions. It generally leads to undefined behavior.

EDIT: Using unions for the purposes of memory reinterpretation (i.e. writing one member and then reading another) was eventually made legal in one of the Technical Corrigendums to C99 standard. Now it is officially OK to do that in C. However, keep in mind that formally this does not protect you from running into undefined behavior by attempting to read a trap representation.
Цель union достаточно очевидна, но по какой-то причине люди это часто упускают.

Целью объединения является сохранение памяти, использую одну область памяти для хранения объектов в разное время. Вот и всё.

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

Именно это делает union. Если Вы знаете, что несколько объектов в Вашей программе хранят значения и у них не пересекается время жизни (lifetime), то Вы можете объединить их в union, тем самым сохранив память. Так же как гостиничный номер имеет не более одного "активного" жильца в каждый момент времени, union имеет не более одного "активного" члена в каждый момент времени. Только "активный" член может быть прочитан. Запись в другой член переключает на него статус "активный".

Использование union для разной интерпретации памяти (запись в одно поле и чтение из другого) был в конечном счете узаконено в одном из технических документов в стандарте C99. Теперь это официально ОК, если это делается в C. Однако, имейте ввиду, что формально это не защитит Вас от UB при работе.
4
15.08.2016, 11:06
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
15.08.2016, 11:06
Помогаю со студенческими работами здесь

Приведение указателя на функцию
Здравствуйте, у меня простейший вопрос, но самому не догнать что-то... В качестве аргумента...

Приведение void* указателя к типу
struct tParamStruct { const char* Result; }; tParamStruct ParamStruct; void show(void...

Приведение void* к типу указателя на структуру
Тема обсуждалась здесь, но решения так и нет нормального Есть два (и более, хоть до 20) линейных...

Приведение указателя на void к другому типу?
в поиске смотрел - не помогло! Возникла проблемка: В книге сказано, что указателю на void можно...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru