Другие языки программирования и технологии

Несколько вопросов про преобразование типов в С++ и возможно не только.

Если Вам какие-то вопросы покажуться некорректны, то пожалуйсто поправьте меня.
За лучший полный и понятный ответ поблагодорю 50 баллов.

1) Неявное преобразование.
а) В каких случаях операнды преобразуются только к "старшему" типу, то есть к типу который занимает больше байт?
б) В каких случаях операнды могут преобразовываться не только к "старшим", но и к "младшим" типам?
в) Могут ли данных целого типа преобразовываться в данные вещественного типа, и наоборот? Например в результате деления, умножения, и т. д. .
г) Для более полного ответа перечислите пожалуйсто случаи когда преобразование будет некоректное?
Приведите пожалуйсто примеры для каждого пункта с комментариями, чтобы было понятно что во что преобразуется.

2) Явное преобразование.
Чем отличается каноническая форма от функциональной, приведите пожалуйсто примеры с коментариями обеих форм, что бы было видно где какая форма, и различие между ними.

3) Иногда я вижу что звёздочка стоит сразу после типа, например
...
int* a;
...
double* b;
...
unsigned short* (выражение)
...
Думаю что это не указатель (ведь в указателях звёздичка стоит непосредственно перед именем) . Что же это такое, и если можно то приведите пожалуйсто примеры с коментариями, что бы было понятно как это работает.
Перво-наперво, и это относится ко всем пунктам, надо сказать, что int(a), (int)a и прочие формы преобразования - это наследство от С и поддерживается оно чисто ради более-менее совместимости с С. И вместо (int)a или int(a) правильно надо писать: static_cast<int>(a). Сам С++ предлагает гораздо больше возможностей по преобразованию типов.
Оператор static_cast<тип>(значение) преобразует значение в тип (если это возможно) по-правильному, т. е. не обязательно побайтно, например static_cast<int>(3.1415926) даст 3 несмотря на то, что у чисел с плавающей точкой и целых чисел формат совершенно разный и "побайтное" преобразование дало бы какой-нибудь глупый результат типа -23868761. Оператор static_cast - считай, замена Сишного стиля.
Есть ещё оператор reinterpret_cast, который даже не преобразует, а скорее "реинтерпретирует", т. е. reinterpret_cast<int>(3.1415926) берет просто первые четыре байта из побайтного представления числа 3.1415926. Это самый "тупой" оператор. Преобразует ВСЁ и В ЛОБ. Пользоваться осторожно. Ну или вот отличие: если наша переменная - объект какого-нибудь класса А и в классе определен оператор преобразования к B, то static_cast< B >(var) вызовет ваш оператор преобразования. А reinterpret не вызовет. А если оператора преобразования нет, static обломится и код не скомпилируется.
Оператор const_cast предназначен только для снятия модификаторов const и volatile. Преобразований не выполняет.
Оператор dynamic_cast выполняет аккуратное динамическое преобразование типов с учётом наследования и реального типа объекта. Вот для сравнения
class A
{
...
}

class B: public A
{
...
}

void func (A *param)
{
ВыводНаЭкран *static_cast< B* >(param);
ВыводНаЭкран *dynamic_cast< B* >(param);
}

void main (void)
{
B* var;
func(var); // Здесь всё нормально
A* var2;
func(var2); // А здесь функция func покажет разный результат в static и dynamic кастах
}

В случае с func(B) переменная var действительно относится к типу B* и поэтому статик и динамик дадут одинаковый результат.
Во втором случае переменная var2 нифига к типу B* не относится, но статик каст тупой и всё равно преобразует (в результате получится ерунда) , а динамик умный и видит, что аргумент не относится к типу B* и выдаст пустой указатель.
Но надо заметить, что динамик пожирает больше процессорного времени.

Итак, собсно ответы на вопросы.
1) а) не совсем понятно. Есть куча "встроенных" преобразований, например int может преобразоваться как в double, так и в char. В общем преобразования вполне интуитивно понятны - любые числа преобразуются в любые.
б) См. а)
в) Могут. int x = 2.71 / 3.14 или float f = 3/4 (правда, в этом случае получится 0, т. к. сначала деление нацело, потом преобразование. Чтобы получилось 0.75, надо писать float f = (float)3 / (float)4).
г) О некорректных преобразованиях тебе сообщит компилятор :) А логически некорректное - см. выше 3/4
2) Что за каноническая форма? Это ты имеешь в виду (int)a и int(a)? Ну на этот счет я дал ответ.
3) это УКАЗАТЕЛЬ. Пробелы/табуляторы до и после звездочки значения не имеют.

Ссылки:
h ttp://alenacpp.blogspot.com/2005/08/c.html
h ttp://forum.vingrad.ru/faq/topic-157739.html
Также заходи на форум forum.codenet.ru
ЮК
Юрий Казуров
54 366
Лучший ответ
при преобразовании от "старшего" типа к "младшему" компилятор будет выдавать предупреждение, т. к. возможна потеря данных, при обратно преобразовании все будет нормально

3. int *a и int* a - вроде одно и тоже
Виктор Дьяков
Виктор Дьяков
4 331