Указатель на икс

Вопрос экспертам: что лучше (и в каких случаях) использовать союз типа

union
{
    uint8  *u8;
    uint16 *u16;
    uint32 *u32;
};


или
void *DataPtr


с явным приведением типа (естественно в применение к эмбеддед системс)? Точнее что безопаснее или какой вариант компилятору (ГЦЦ) больше по вкусу?
ЗЫ… форумы мне не интересны

Комментарии (15)

RSS свернуть / развернуть
Лучше писать программу так, чтобы подобные конструкции не требовались. То-есть вариант «ТИП *DataPtr» с явным приведением, при необходимости. И чем реже производится приведение, тем лучше.
0
  • avatar
  • m0xf
  • 14 апреля 2013, 17:44
… в том то и дело, что разным задачам разные данные будут передаватся, точнее одной и той же задаче… но разные данные в разное время.
0
Опишите подробнее) Вдруг, там проще можно Оо
0
… да я и сам толком не продумал программу… просто интересует данный вопрос… с союзами разных типов работал, а вот с союзами указателей нет и как-то ссыкотно :)
ЗЫ… "нечеткие желания ведут к нечетким результатам"… какой-то старый китаец
0
Если честно, в союзах указателей самих по себе я смысла какого-то не вижу.

Вообще, это очень странно — передавать массивы с разным размером элемента в одну и ту же функцию =)
0
Вообще, это очень странно — передавать массивы с разным размером элемента в одну и ту же функцию =)
… ну это я так, гипотетически :))
0
Мое мнение, что:
1) Надо всеми силами избегать подобного подхода.
2) Если избежать не получается, то использовать void* и приведене. При чтении кода «другим» программистом, он не будет строить догадок о том, что храниться в памяти. И только в месте явного приведения, будет понимать, что вот тут все и определено.

З.Ы. ИМХО, на основе своего опыта.
0
… благодарю за совет, наверное так и поступлю
0
ну почему же. если юнион будет именованным, то вполне читаемые и понятные происхождения
char c = data.asU8[0];    // первый байт массива
short s = data.asU16[1];    // 2 байта начиная со 2-го
long l = data.asU32[1];    // 4 байта начиная с 4-го (дада, выравнивение не забываем)

Хороший пример получился. Демонстрирует неразбериху с индексами при извлечении данных из сырого буффера.

Хорошим вариантом будет использование void/u8-указателя, и набор функций/макросов для извлечения данных требуемого формата. В них, к тому же, можно будет предусмотреть разный порядок байт (если конечно вопрос в передаче сырых данных между системами).

Если же вопрос в передаче «некой разной структуры в одну фукцию», то void-указатель, приводимый к нужному типу, я предпочту. По причине более простой расширяемости функционала — не надо трогать определение union'а при добавление нового функционала.
0
short s = data.asU16[1];    // 2 байта начиная со 2-го
long l = data.asU32[1];    // 4 байта начиная с 4-го (дада, выравнивение не забываем)
С третьего/пятого, на самом деле. Ну или в первой строке — нулевой байт, а не первый.
0
Ага, сначала порядковый номер использовал, а затем привычные индексные. лажа.
0
Вполне очевидно, что vectorи vector — это разные классы. Поэтому в данном случае следует использовать машинозависимые методы доступа к массиву данных. Например, если это ARM, а массив располагается во flash-памяти, то доступ к flash-памяти производится 16-битными словами, читается ли байт, 2 байта или 4. Но C++ — это язык высокого уровня, поэтому операции чтения/записи следует свести к оператору =, поскольку они наиболее оптимально реализованы на уровне компилятора.
Итак, нам нужно получить что-то вроде:
uint8 a = (uint8) V[n];
uint16 b = (uint16) V[n];
uint32 c = (uint32) V[n];

Получаем следующий метод в классе (назовём его Vector):
class Vector
{
public:
    template <typename _T>
    inline _T& operator [] (unsigned int n) {
        _T *p = (_T*) this;
        return p[n];
    }
};

И далее используем его следующим образом:
uint8 data[256];
Vector* pV = (Vector*) data;
uint32 lval = 0;
uint16 sval = 0;
uint8 cval = 0;
for (int n = 0; n < 10; ++n) {
    lval = (*pV)[n];
    sval = (*pV)[n];
    cval = (*pV)[n];
    (*pV)[n] += lval;
    (*pV)[n] += sval;
    (*pV)[n] += cval;
}
0
ИМХО, Ваш код плох тем, что не дает подсказки программисту, что тут идет манипуляция сырыми данными в памяти. Я уже писал выше, что или надо избегать подобного стиля, или явно указывать, что тут идет тонкая работа с данными. И делать какие-либо предположения нельзя.
З.Ы. В Вашем примере, гораздо лучше и проще написать:
uint32 *lptr = (uint32 *)data;…
Это, одновременно, не даст обратиться к данным не того типа (будет предупреждение, что Вы слово пытаетесь положить в байт). У Вас же, (*pV)[n] это все что угодно (байт, слово, двойное слово) и из кода это не очевидно и компилятор это пропустит.

Все выше сказанное, ИМХО.
+1
тут может быть проблема, если кто-то захочет взять пару младших байт от 4-го DWORD'а, что обычно делается как
sval = (u16)(*pV)[3];
или младший байт от 3-го WORD'а
cval = (*pV)[3]&0x0ff; // а что тут хотели получить?
0
Если предположить, что подобной ситуации избежать нельзя, то я бы выбрал union. Там, в каком-то виде, остается контроль типа.

А вообще, с такими манипуляциями нужно быть очень осторожным. Например, ARM7TDMI не может обращается к двойному слову (32-битной переменной) ели адрес не выравнен на границу двойного слова. Берем произвольный адрес, приводим к DWORD, пытаемся прочитать — и с большой вероятностью получаем DATA ABORT
+2
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.