А>Почему массив автоматически преобразуется в указатель на его первый элемент, а обратное преобразование требует целый reinterpret_cast<> ?
Это называется "правило распада". А происходит это потому, что ко всякому массиву можно обратится через указатель. Но совсем не всякий указатель указывает на первый элементо массива!
Здравствуйте, Аноним, Вы писали:
А>Почему массив автоматически преобразуется в указатель на его первый элемент, а обратное преобразование требует целый reinterpret_cast<> ?
Потому что массив это на самом деле просто кусок памяти заданного размера (в данном случае под заданное число элементов). Т.е. размер такого массива является статической информацией, доступной только при компиляции, и не существующей во время выполнения (конечно если Вы её никуда не сохранили). Делая каст, Вы фактически пытаетесь восстановить эту контекстную информацию.
void foo()
{
int a[5];
int* p = a;
int (&r)[5] = a;
int (&r1)[4] = ptrToArray<4>( p + 1 );
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, achp, Вы писали:
А>>>обратное преобразование требует целый reinterpret_cast<> ? A>>Э… А можно конкретный пример такого reinterpret_cast?
E>А в чём проблема? E>
...
Егор, мы знаем, что ты знаешь
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Егор, мы знаем, что ты знаешь
Людям вроде как пример было надо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, rg45, Вы писали:
R>>Егор, мы знаем, что ты знаешь
E>Людям вроде как пример было надо...
Просто хотелось убедиться, что автор топика ничего не путает — не пытается вместо ссылки на массив получить указатель какого либо типа, или массив как временный объект (кастит к rvalue массиву). Ведь ни для кого не секрет, что концепции массивов-указателей зачастую вызывают затруднения у начинающих.
--
Справедливость выше закона. А человечность выше справедливости.
В этом примере функция Parse() содержит ошибку в строке #1-выход за границу буфера. Компилятор ее, естественно, не обнаруживает, так как он понятия не имеет, какой длины у нас Buf. Для того, чтобы застраховаться от подобных ошибок, я хотел сделать так:
enum {
bBufSize=5
};
typedef char TBufArr[bBufSize];
TData Parse(const TBufArr &Buf)
{ // Тело функции без изменений
TData d;
d.dw=MakeInt(Buf[0], Buf[1], Buf[2], Buf[3]);
d.b =Buf[4];
d.b1=Buf[5]; // #1return d;
}
TData Read(const char* Msg, int nSize)
{
if (!Msg) throw"Zero pointer";
if (nSize<bBufSize) throw"Small buffer";
const TBufArr &BufArr=reinterpret_cast<const TBufArr&>(Msg); // #2return Parse(BufArr);
}
Теперь компилятору уже явно видно, что мы лезем за границу массива, и он может об этом предупредить.
Меня удивило то, что потребовался reinterpret_cast. Я думал, static_cast-а будет достаточно. По аналогии с наследованием классов: в одну сторону преобразование всегда возможно и, поэтому, выполняется автоматически, а в обратную сторону преобразование возможно, но не всегда, поэтому требуется разрешение программиста: static_cast (но не reinterpret_cast!).
Но меня ждал еще один сюрприз: MSVC все равно не сообщает об ошибке, так что вся эта затея обломилась. Как-то можно заставить компилятор замечать такие явные ошибки?
Еще обнаружил, что если строку #2 слегка изменить:
Здравствуйте, Tscheineg, Вы писали:
T>Но меня ждал еще один сюрприз: MSVC все равно не сообщает об ошибке, так что вся эта затея обломилась. Как-то можно заставить компилятор замечать такие явные ошибки?
Есть смысл отказаться от голого сишного массива char(&)[N] в пользу полноценного типа boost::array<char,N> или чего-то в таком роде.
Тогда можно заставить ошибки ловиться в рантайме.
T>то возникает ошибка error C2102: '&' requires l-value T>В чем тут дело?
Дело в том, что реинтерпрет ссылки — это попытка трактовать переменную, а не её значение.
Msg+1 — это значение типа char*, существующее только мысленно.
Msg[1] — это переменная типа char, находящаяся по адресу Msg+1
Вот (ссылку на) эту переменную (плюс следующие за ней несколько байтов) уже можно трактовать как (ссылку на) переменную иного типа.
А (указатель на) — можно трактовать как (указатель на).
То есть
const TBufArr* pBuf = reinterpret_cast<const TBufArr*>( Msg+1 );
const TBufArr& rBuf = *pBuf // или, что то же самое,
*reinterpret_cast<const TBufArr*>( Msg+1 )
reinterpret_cast<const TBufArr&>(*(Msg+1) )
reinterpret_cast<const TBufArr&>( Msg[1] )
Здравствуйте, Кодт, Вы писали:
К>Есть смысл отказаться от голого сишного массива char(&)[N] в пользу полноценного типа boost::array<char,N> или чего-то в таком роде. К>Тогда можно заставить ошибки ловиться в рантайме.
Про boost::array я помню, но он будет ловить эти ошибки во время выполнения, а не компиляции.
Вот пара примеров, когда MSVC 2008 c 4 уровнем дает предупреждение: warning C4700: uninitialized local variable 'a' used
А)
int a[3];
int b=a[10];
Б)
int a[3];
a[9]=9;
int b=a[10];
А вот примеры, когда компилится без предупреждений:
В)
int a[3];
a[10]=10;
int b=a[10];
Г)
int a[3]={};
int b=a[10];
То есть, компилятор знает размер массива и даже следит за тем, какие элементы массива инициализированы, а какие — нет (см. примеры Б и В). Так что ему мешает в этих явных случаях выдавать хотя бы предупреждение о выходе за границы?