Преобразование к типу указателя
От: b_mike  
Дата: 31.03.12 17:19
Оценка:
Здравствуйте. Я новичок.Сейчас пытаюсь разобраться с темой указателей. В книге которую читаю есть пример "выбор данных из памяти с помощью разных указателей"
unsigned long L = 0x12345678L;
char *cp = (char*)&L;
int* ip = (int*)&L;
cout<<hex;
сout<<"&L = "<<&L<<endl;
cout<<"cp = "<<(void*)cp<<"\t *cp = 0x"<<*cp<<endl;
сout<<"ip = "<<(void*)ip<<"\t *ip = 0x"<<*ip<<endl;

соответственно результат выполнения:
&L = 0x1E190FFC 
cp = 0x1E190FFC         *cp = 0x78
ip = 0x1E190FFC         *ip = 0x5678

Когда речь идет о преобразовании обычных типов вроде все ясно(для чего и что происходит).
А вот когда вижу
char *cp = (char*)&L;
int* ip = (int*)&L;

Мне не совсем понятно что фактически происходит в преобразовании (char*)&L или (int*)&L. В приведенном выше примере результат программы показывает что адрес L и значение указателей cp и ip фактически одинаково = 0x1E190FFC. Это ведь адрес ячейки памяти, зачем его приводить к типу? При этом как я понимаю что значением указателя ip например, является адрес переменной L и при обращении к ее значению через операцию разыменования (*ip) из 4-х байт переменной L будет выбрано 2 байта информации поскольку указатель имеет тип int. Это демонстрирует и результат программы. Что происходит фактически с адресом &L когда его приводят к типу (char*) или (int*) и происходит ли вообще??? Он ведь остается прежним! Поясните пожалуйста момент приведения к типу указателя. Заранее спасибо.
Re: Преобразование к типу указателя
От: Сыроежка  
Дата: 31.03.12 17:27
Оценка:
Здравствуйте, b_mike, Вы писали:

_>Здравствуйте. Я новичок.Сейчас пытаюсь разобраться с темой указателей. В книге которую читаю есть пример "выбор данных из памяти с помощью разных указателей"

_>
_>unsigned long L = 0x12345678L;
_>char *cp = (char*)&L;
_>int* ip = (int*)&L;
_>cout<<hex;
_>сout<<"&L = "<<&L<<endl;
_>cout<<"cp = "<<(void*)cp<<"\t *cp = 0x"<<*cp<<endl;
_>сout<<"ip = "<<(void*)ip<<"\t *ip = 0x"<<*ip<<endl;

_>соответственно результат выполнения:
_>&L = 0x1E190FFC 
_>cp = 0x1E190FFC         *cp = 0x78
_>ip = 0x1E190FFC         *ip = 0x5678
_>

_>Когда речь идет о преобразовании обычных типов вроде все ясно(для чего и что происходит).
_>А вот когда вижу
_>
_>char *cp = (char*)&L;
_>int* ip = (int*)&L;
_>

_>Мне не совсем понятно что фактически происходит в преобразовании (char*)&L или (int*)&L. В приведенном выше примере результат программы показывает что адрес L и значение указателей cp и ip фактически одинаково = 0x1E190FFC. Это ведь адрес ячейки памяти, зачем его приводить к типу? При этом как я понимаю что значением указателя ip например, является адрес переменной L и при обращении к ее значению через операцию разыменования (*ip) из 4-х байт переменной L будет выбрано 2 байта информации поскольку указатель имеет тип int. Это демонстрирует и результат программы. Что происходит фактически с адресом &L когда его приводят к типу (char*) или (int*) и происходит ли вообще??? Он ведь остается прежним! Поясните пожалуйста момент приведения к типу указателя. Заранее спасибо.

Адрес остается прежним, но зато меняется интерпретация этих данных по этому адресу, а также меняется арифметика указателей, так как каждый раз вы указываете определенный тип указателя.
Например, если у вас есть предложение из вашего примера


char *cp = (char*)&L;

то ++cp сместиться только на размер байта. То есть вы побайтно сможете анализировать значения, расположенные в исходном объекте.

Другой пример.

int x = -10;
unsigned int *p = ( unsigned int * ) &x;

В этом случае вы уже можете манипулировать значением x через указатель p, как будто бы оно беззнаковое целое число.
Меня можно встретить на www.cpp.forum24.ru
Re: Преобразование к типу указателя
От: okman Беларусь https://searchinform.ru/
Дата: 31.03.12 18:56
Оценка:
Здравствуйте, b_mike, Вы писали:

_>Здравствуйте. Я новичок.Сейчас пытаюсь разобраться с темой указателей. В книге которую читаю есть пример "выбор данных из памяти с помощью разных указателей"

_>...
_>Мне не совсем понятно что фактически происходит в преобразовании (char*)&L или (int*)&L. В приведенном выше примере результат программы показывает что адрес L и значение указателей cp и ip фактически одинаково = 0x1E190FFC. Это ведь адрес ячейки памяти, зачем его приводить к типу?

Это называется типобезопасность.
Поразмышляйте над следующим псевдокодом:
unsigned int SomeValue = 0xFFFFFFFF;
int AnotherValue = SomeValue;

Вопрос — чему будет равно AnotherValue ? Будет равно -1. Потому что SomeValue — это беззнаковое
целое, а AnotherValue — целое со знаком, во время присваивания произошло преобразование типов, а
значение 0xFFFFFFFF в знаковой форме соответствует минус единице. Подобные "фокусы" несут в себе
много потенциальных опасностей, поэтому компилятор не даст скомпилироваться данному примеру и
покажет сообщение об ошибке, что-то вроде "cannot convert from <int> to <unsigned int>".

Но если мы точно знаем, что делаем, мы можем разрешить подобное приведение типов явным образом:
unsigned int SomeValue = 0xFFFFFFFF;
int AnotherValue = (int)SomeValue;

Здесь мы явно указываем, что соглашаемся с последствиями, к которым может привести
преобразование unsigned int в int. Такая форма называется приведением в стиле C и
считается опасной, в первую очередь потому, что позволяет без разбору выполнять
приведения несовместимых между собой типов. Например, указатель в char, int в void * и т.д.
Когда-нибудь исходный тип может измениться, но компилятор спокойно проглотит его
приведение в несовместимую форму где-нибудь в совершенно другой части кода, и из-за
этого полезут противные и труднообнаруживаемые баги.

В C++ рекомендуется использовать новые механизмы приведения типов — это static_cast, в
первую очередь, а также dynamic_cast, const_cast и reinterpret_cast.
static_cast не позволяет "просто так" привести указатель в int, например, и
считается намного более безопасным, чем обычное приведение в стиле C.
С учетом сказанного, код лучше переписать так:
unsigned int SomeValue = 0xFFFFFFFF;
int AnotherValue = static_cast<int>(SomeValue);


В Вашем примере речь идет об указателях.
Здесь нужно иметь в виду дополнительное условие — арифметика указателей.
Указатель на char предназначен для доступа к char-ам, указатель на int — для
доступа к int-ам и т.д. Если мы через указатель на char прочитаем int-овское
значение, то получим только ту его часть, которая укладывается в размер char.
Если мы через указатель на int попробуем записать что-нибудь в char-переменную, то,
скорее всего, словим ошибку доступа к памяти и программа будет завершена аварийно.
Либо попортим соседнюю переменную или участок кода и вообще вылетим неизвестно куда.

А инкремент указателя приводит к увеличению его скалярного значения на размер типа.
Например, ++<char> делает инкремент на sizeof (char) байт, ++<int> на sizeof (int) байт,
++<my_class> на sizeof (my_class) байт и т.д. Если указатель на int установить на
массив char-ов, а затем попытаться "пройтись" по этому массиву, инкрементируя
указатель на каждой итерации, мы получим неверные значения, а то и вовсе ошибку доступа,
потому что указатель каждый раз будет увеличиваться не на <char> байт, как должен был бы, а
на <int> байт.

Ввиду обозначенных причин компилятор также не посмеет "бездумно" приводить один
тип указателя к другому, ибо их арифметика отличается.

В заключение следует отметить, что приведения типов вроде тех, которые были продемонстрированы,
не несут никаких накладных расходов и сгенерированные машинные команды получаются такими,
как если бы значения присваивались напрямую, без приведений типов. Сами приведения нужны
только для обеспечения удобств программирования, точно так же, как public/private или typedef.
Re: Преобразование к типу указателя
От: MasterZiv СССР  
Дата: 31.03.12 20:22
Оценка:
On 03/31/2012 09:19 PM, b_mike wrote:

> Когда речь идет о преобразовании обычных типов вроде все ясно(для чего и что

> происходит).
> А вот когда вижу
>
> char *cp = (char*)&L;
> int* ip = (int*)&L;

А чем тебе char & int необычны ? Это -- обычные типы языка С/С++.

Это ведь адрес
> ячейки памяти, зачем его приводить к типу?

Чтобы компилятор интерпретировал этот адрес (один и тот же) в дальнейшем как
указатель на переменную определённого типа.

При этом как я понимаю что значением
> указателя ip например, является адрес переменной L и при обращении к ее значению
> через операцию разыменования (*ip) из 4-х байт переменной L будет выбрано 2
> байта информации поскольку указатель имеет тип int.

int и long в современных платформах оба 4 байта.


Это демонстрирует и
> результат программы. Что происходит фактически с адресом &L когда его приводят к
> типу (char*) или (int*) и происходит ли вообще???

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

(char*) -- будет считать, что там лежит код символа (8-битное число)
(int*) -- будет считать, что там лежит целое число, как правило, 4 байтовое,
знаковое.
Posted via RSDN NNTP Server 2.1 beta
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.