Массив и указатель: преобразование
От: Аноним  
Дата: 16.09.10 13:50
Оценка: :)
Почему массив автоматически преобразуется в указатель на его первый элемент, а обратное преобразование требует целый reinterpret_cast<> ?
Re: Массив и указатель: преобразование
От: Vamp Россия  
Дата: 16.09.10 13:52
Оценка:
А>Почему массив автоматически преобразуется в указатель на его первый элемент, а обратное преобразование требует целый reinterpret_cast<> ?
Это называется "правило распада". А происходит это потому, что ко всякому массиву можно обратится через указатель. Но совсем не всякий указатель указывает на первый элементо массива!
Да здравствует мыло душистое и веревка пушистая.
Re: Массив и указатель: преобразование
От: Мишень-сан  
Дата: 16.09.10 13:59
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Почему массив автоматически преобразуется в указатель на его первый элемент, а обратное преобразование требует целый reinterpret_cast<> ?


Потому что массив это на самом деле просто кусок памяти заданного размера (в данном случае под заданное число элементов). Т.е. размер такого массива является статической информацией, доступной только при компиляции, и не существующей во время выполнения (конечно если Вы её никуда не сохранили). Делая каст, Вы фактически пытаетесь восстановить эту контекстную информацию.
Re: Массив и указатель: преобразование
От: achp  
Дата: 16.09.10 16:07
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>обратное преобразование требует целый reinterpret_cast<> ?


Э… А можно конкретный пример такого reinterpret_cast?
Re[2]: Массив и указатель: преобразование
От: Erop Россия  
Дата: 16.09.10 16:27
Оценка:
Здравствуйте, achp, Вы писали:

А>>обратное преобразование требует целый reinterpret_cast<> ?

A>Э… А можно конкретный пример такого reinterpret_cast?

А в чём проблема?
template<typename T> struct RefOf{ typedef T& Type; };
template<int N, typename T> typename RefOf<T[N]>::Type ptrToArray( T* ptr )
{
    return *reinterpret_cast<T(*)[N]>( ptr );
}
ну и пример использования:
void foo()
{
    int a[5];
    
    int* p = a;
    int (&r)[5] = a;
    int (&r1)[4] = ptrToArray<4>( p + 1 );
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Массив и указатель: преобразование
От: Vamp Россия  
Дата: 16.09.10 16:39
Оценка: :)
A>Э… А можно конкретный пример такого reinterpret_cast?
Всегда пожалуйста:


int main() {
   typedef int arr_t [20];
   arr_t arr;
   arr_t* parr = &arr;
   int* i = *parr; 

   //parr = &i; //error

   parr = reinterpret_cast<arr_t*>(i); //OK


}
Да здравствует мыло душистое и веревка пушистая.
Re[3]: Массив и указатель: преобразование
От: rg45 СССР  
Дата: 17.09.10 04:54
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, achp, Вы писали:


А>>>обратное преобразование требует целый reinterpret_cast<> ?

A>>Э… А можно конкретный пример такого reinterpret_cast?

E>А в чём проблема?

E>
...


Егор, мы знаем, что ты знаешь
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: Массив и указатель: преобразование
От: Erop Россия  
Дата: 17.09.10 05:21
Оценка:
Здравствуйте, rg45, Вы писали:

R>Егор, мы знаем, что ты знаешь


Людям вроде как пример было надо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Массив и указатель: преобразование
От: rg45 СССР  
Дата: 17.09.10 07:06
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, rg45, Вы писали:


R>>Егор, мы знаем, что ты знаешь


E>Людям вроде как пример было надо...


Просто хотелось убедиться, что автор топика ничего не путает — не пытается вместо ссылки на массив получить указатель какого либо типа, или массив как временный объект (кастит к rvalue массиву). Ведь ни для кого не секрет, что концепции массивов-указателей зачастую вызывают затруднения у начинающих.
--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: Массив и указатель: преобразование
От: Tscheineg  
Дата: 17.09.10 09:14
Оценка:
Блин, анонимно можно только начать тему, продолжать ее нельзя.

Задача такая. Есть функция Read(), которая читает последовательность байтов и возвращает осмысленную структуру TData.
int MakeInt(char ch0, char ch1, char ch2, char ch3);

struct TData {
  int  dw;
  char b;
  char b1;
};

// Распознает 5-и байтный буфер
TData Parse(const char* Buf)
{
  TData d;
  d.dw=MakeInt(Buf[0], Buf[1], Buf[2], Buf[3]);
  d.b =Buf[4];
  d.b1=Buf[5]; // #1
  return d;
}

TData Read(const char* Msg, int nSize)
{
  if (!Msg)           throw "Zero pointer";
  if (nSize<bBufSize) throw "Small buffer";
  return Parse(Msg);
}

В этом примере функция 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]; // #1
  return 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); // #2
  return Parse(BufArr);
}

Теперь компилятору уже явно видно, что мы лезем за границу массива, и он может об этом предупредить.

Меня удивило то, что потребовался reinterpret_cast. Я думал, static_cast-а будет достаточно. По аналогии с наследованием классов: в одну сторону преобразование всегда возможно и, поэтому, выполняется автоматически, а в обратную сторону преобразование возможно, но не всегда, поэтому требуется разрешение программиста: static_cast (но не reinterpret_cast!).

Но меня ждал еще один сюрприз: MSVC все равно не сообщает об ошибке, так что вся эта затея обломилась. Как-то можно заставить компилятор замечать такие явные ошибки?

Еще обнаружил, что если строку #2 слегка изменить:
const TBufArr &BufArr=reinterpret_cast<const TBufArr&>(Msg+1);

то возникает ошибка error C2102: '&' requires l-value
В чем тут дело?
Re[7]: Массив и указатель: преобразование
От: Кодт Россия  
Дата: 17.09.10 09:56
Оценка:
Здравствуйте, Tscheineg, Вы писали:

T>Но меня ждал еще один сюрприз: MSVC все равно не сообщает об ошибке, так что вся эта затея обломилась. Как-то можно заставить компилятор замечать такие явные ошибки?


Есть смысл отказаться от голого сишного массива char(&)[N] в пользу полноценного типа boost::array<char,N> или чего-то в таком роде.
Тогда можно заставить ошибки ловиться в рантайме.
template<class T>
class proxy_array
{
  T* arr_;
  size_t n_;
public:
  proxy_array(T* arr, size_t n) : arr_(arr), n_(n) {}

  T& operator[](int i) const { assert(i>=0 && i<n_); return arr_[i]; }
                               /////////////////////
  T* data() const { return arr_; }
  size_t size() const { return n_; }

  // при желании - можно прикрутить итераторы с отладочными средствами
};

TData Parse(proxy_array<char> Buf)
{
  int i=0;
  char c0 = Buf[i++];
  char c1 = Buf[i++];
  ....
  char c5 = Buf[i++]; // assertion fault!
  ....
}

TData Read(char* Msg, int Len)
{
  return Parse(proxy_array<char>(Msg,Len));
}


T>Еще обнаружил, что если строку #2 слегка изменить:

T>
const TBufArr &BufArr=reinterpret_cast<const TBufArr&>(Msg+1);

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] )
Перекуём баги на фичи!
Re[3]: Массив и указатель: преобразование
От: Кодт Россия  
Дата: 17.09.10 09:57
Оценка: +1 :)))
Здравствуйте, Erop, Вы писали:

E>template<typename T> struct RefOf{ typedef T& Type; };
E>template<int N, typename T> typename RefOf<T[N]>::Type ptrToArray( T* ptr )

Не умеешь ты народ пугать. Вот как надо:
template<int N, typename T> T(&ptrToArray(T*p))[N] { return *(T(*)[N])p; }
Перекуём баги на фичи!
Re[8]: Массив и указатель: преобразование
От: Tscheineg  
Дата: 17.09.10 12:32
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Есть смысл отказаться от голого сишного массива 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];


То есть, компилятор знает размер массива и даже следит за тем, какие элементы массива инициализированы, а какие — нет (см. примеры Б и В). Так что ему мешает в этих явных случаях выдавать хотя бы предупреждение о выходе за границы?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.