Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 30.08.2014, 01:14   #1
220Volt
Форумчанин
 
Регистрация: 14.12.2012
Сообщений: 668
По умолчанию

Добрый день.
Начитался про ужасы aliasing'a, решил изучить вопрос. Ни одного раза не удалось воспроизвести проблемные коды, которые имеются в сети. Возможно gcc поумнел. Например, накидал такой код:
Код:
int fnj5555555555(int *val, int count)
{
    short *al = reinterpret_cast<short*>(val);
    for(int i=0; i<count; ++i){
        *val += i;
        ++ *al;
    }
    return *val;
}
}
Здесь ведь должны быть проблемы, верно? Но нет, все работает как ожидалось:
Код:
// gcc 1.cpp -O3
for(){
    L15:
        movl	24(%esp), %edx      // разыменовываем val в edx
    L12:
        addl	%eax, %edx          // edx += i
        addl	$1, %eax            // ++ i
        movl	%edx, 24(%esp)      // сбрасываем edx в память
        addw	$1, 24(%esp)        // ++ *al
        cmpl	%ecx, %eax
        jne	L15
А без al, компилятор избегает сброс из регистра в память на каждой итерации:
Код:
int fnj5555555555(int *val, int count)
{
    for(int i=0; i<count; ++i){
        *val += i;
    }
    return *val;
}
L10:
	addl	%eax, %edx
	addl	$1, %eax
	cmpl	%ecx, %eax
	jne	L10
Естественно я пробовал различные коды, это один из них. В общем, опыты провалились.

Чего я хочу? Хочу понять - как правильно создавать несколько указателей разного (сильно) типа на один объект. Везде о говорят об использовании union'ов, но в справочнике написано, что обращение к неактивному полю union - UB, это расширение компиляторов.

Из справочника http://en.cppreference.com/w/cpp/lan...interpret_cast:
Код:
Type aliasing

When a pointer or reference to object of type T1 is reinterpret_cast 
(or C-style cast) to a pointer or reference to object of a different 
type T2, the cast always succeeds, but the resulting pointer or reference 
may only be accessed if one of the following is true:
 1. T2 is the (possibly cv-qualified) dynamic type of the object 
 2. T2 and T1 are both (possibly multi-level, possibly cv-qualified at each 
    level) pointers to the same type T3 (since C++11) 
 3. T2 is the (possibly cv-qualified) signed or unsigned variant of the 
    dynamic type of the object 
 4. T2 is an aggregate type or a union type which holds one of the 
    aforementioned types as an element or non-static member (including, 
    recursively, elements of subaggregates and non-static data members of the 
    contained unions): this makes it safe to cast from the first member of a 
    struct and from an element of a union to the struct/union that contains it. 
 5. T2 is a (possibly cv-qualified) base class of the dynamic type of the object 
 6. T2 is char or unsigned char 

If T2 does not satisfy these requirements, accessing the object through the new pointer 
or reference invokes undefined behavior. This is known as the strict aliasing rule and 
applies to both C++ and C programming languages. 

Note that many compilers implement disable this rule, as a non-standard language extension, 
if the wrong-type access is made through the inactive member of a union.
Может кто-нибудь объяснить/показать пример пункта 4? У меня есть некоторые мысли, но не уверен.

Последний раз редактировалось Stilet; 30.08.2014 в 09:00.
220Volt вне форума Ответить с цитированием
Старый 30.08.2014, 07:27   #2
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,330
По умолчанию

Цитата:
Сообщение от 220Volt Посмотреть сообщение
в справочнике написано, что обращение к неактивному полю union - UB, это расширение компиляторов.
Я лично эту фразу вообще не понял - можно подробней, что за справочник, что именно там написано?

Если очень надо, каст всегда можно делать либо через С каст - (type) expression, либо (в случае с указателями) через void*. Естественно, размер указателей должен совпадать с размер void указателя.

Зачем делать *очень* разные по типу указатели на один и тот же объект?
Откуда например будет браться тип объекта? Не лучше ли использовать виртуальный методы?

Последний раз редактировалось waleri; 30.08.2014 в 07:30.
waleri вне форума Ответить с цитированием
Старый 30.08.2014, 13:11   #3
220Volt
Форумчанин
 
Регистрация: 14.12.2012
Сообщений: 668
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
Я лично эту фразу вообще не понял - можно подробней, что за справочник, что именно там написано?

Если очень надо, каст всегда можно делать либо через С каст - (type) expression, либо (в случае с указателями) через void*. Естественно, размер указателей должен совпадать с размер void указателя.

Зачем делать *очень* разные по типу указатели на один и тот же объект?
Откуда например будет браться тип объекта? Не лучше ли использовать виртуальный методы?
Справочная страница по ссылке выше:
"Заметьте, что многие компиляторы реализуют отключение этого правила
(type aliasing rule), как нестандартное расширение языка если неправильный доступ к типу произведен через неактивный член union (неактивный член - член, чтение которого не следует за записью в тот же член)."
"Многие компиляторы реализуют, как нестандартное расширение языка, возможность чтения неактивного члена union."

В общем, стандарт не ограничивает компилятор при оптимизации union'в.
Как следствие, при написании следующего:
Код:
typedef union
{
  uint32_t u32;
  uint16_t u16[2];
}
U32;

U32      in;
uint16_t lo;
uint16_t hi;

in.u32    = 654;
hi        = in.u16[0];
lo        = in.u16[1];
компилятор имеет полное право при "in.u32 = 654" произвести записать в регистр, а запись в hi и lo из памяти. А может и вообще "in.u32 = 654" вырежет, как бессмысленную (на его взгляда) команду.

Последний раз редактировалось 220Volt; 30.08.2014 в 13:13.
220Volt вне форума Ответить с цитированием
Старый 30.08.2014, 16:00   #4
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

А можно для любопытствующих ссылку на русский ресурс рассказывающий что это такое Type aliasing?
I'm learning to live...
Stilet вне форума Ответить с цитированием
Старый 30.08.2014, 16:16   #5
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,330
По умолчанию

А при чем тут юнион, разве это не относится к записи любых переменных?
waleri вне форума Ответить с цитированием
Старый 30.08.2014, 16:47   #6
220Volt
Форумчанин
 
Регистрация: 14.12.2012
Сообщений: 668
По умолчанию

Цитата:
Сообщение от Stilet Посмотреть сообщение
А можно для любопытствующих ссылку на русский ресурс рассказывающий что это такое Type aliasing?
http://habrahabr.ru/post/114117/

Цитата:
Сообщение от waleri Посмотреть сообщение
А при чем тут юнион, разве это не относится к записи любых переменных?
На ваш взгляд, применение union подобным способом не решит часть вопросов при которых может прийти в голову делать reinterpret_cast?
Вот здесь товарищ не плохо развернуто ответил, правда на английском http://stackoverflow.com/questions/9...le/99010#99010. Но такое использование union не попадает под стандарт.

Последний раз редактировалось Stilet; 30.08.2014 в 17:10.
220Volt вне форума Ответить с цитированием
Старый 30.08.2014, 17:12   #7
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

Цитата:
220Volt
Спс. почитаемс.
I'm learning to live...
Stilet вне форума Ответить с цитированием
Старый 30.08.2014, 20:04   #8
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,330
По умолчанию

Цитата:
Сообщение от 220Volt Посмотреть сообщение
Но такое использование union не попадает под стандарт.
Там вроде написано "This is allowed in C99 and explicitly allowed in C11."

Не так страшен черт как его малюют. Ведь если все было так, никогда нельзя будет прочитать никакие данные, ведь получение адреса на int переменную - тот же aliasing.
waleri вне форума Ответить с цитированием
Старый 30.08.2014, 21:13   #9
220Volt
Форумчанин
 
Регистрация: 14.12.2012
Сообщений: 668
По умолчанию

Вот еще неплохая статья: http://dbp-consulting.com/StrictAliasing.pdf

Вроде разобрался, резюмировал (от себя ничего не добавлял, только комментарии):
Код:
Допустимое преобразование с T1* в Т2*, если T2:
- тип совместимый с T1,
- квалифицированная версия T1 (const, volatile),
- signed или unsigned версия T1,
- signed или unsigned версия квалифицированного T1,
- агрегатный или union включающий один из вышеупомянутых типов среди членов,
- символьный тип (char, unsigned char* или signed char*).
Иначе undefined behavior.
  
Напишем различными способами функцию, которая меняет половинки 32 битного целого:
    1. Невалидная версия:
        uint32_t swaphalves(uint32_t a)
        {
            uint32_t acopy=a;
            uint16_t *ptr=(uint16_t*)&acopy;
            uint16_t tmp=ptr[0];
            ptr[0]=ptr[1];
            ptr[1]=tmp;
            return acopy;
        }
    2. Union версия. Валидная для Си, но не гарантированно валидная для С++:
        uint32_t swaphalves(uint32_t a)
        {
            typedef union {
                uint32_t as32bit;
                uint16_t as16bit[2];
            } swapem;
            swapem s={a};
            uint16_t tmp;
            tmp=s.as16bit[0];
            s.as16bit[0]=s.as16bit[1];
            s.as16bit[1]=tmp;
            return s.as32bit;
        }
    3. memcpy версия, валидная для Си и С++:
        uint32_t swaphalves(uint32_t a)
        {
            uint16_t as16bit[2],tmp;
            memcpy(as16bit, &a, sizeof(a));
            tmp = as16bit[0];
            as16bit[0] = as16bit[1];
            as16bit[1] = tmp;
            memcpy(&a, as16bit, sizeof(a));
            return a;
        }
И не забываем про restrict (особенно актуально для MSVC) http://en.cppreference.com/w/c/keyword/restrict описание выше, по ссылке на хабр.

Последний раз редактировалось 220Volt; 31.08.2014 в 01:02.
220Volt вне форума Ответить с цитированием
Старый 30.08.2014, 21:46   #10
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

Хм... Если честно то я для себя нашел еще одну причину поменьше использовать открытые указатели... Впрочем я не в курсах как действует компиль паскаля в подобных ситуациях. Это я типа оффтоплю...
I'm learning to live...
Stilet вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
С++ not implemented in type 'istream' for arguments of type 'float *'из-за чего эта ошибка и как исправить? Mitax-47 Помощь студентам 1 10.05.2013 15:48
Could not convert variant of the type (String) into type (Boolean) Silly Student C++ Builder 0 19.11.2011 13:06
Отличие type T = Object от type T = Class? Warn Общие вопросы Delphi 8 04.11.2011 19:20
Could not convert variant of type (Olestr) into type (Double) java_91 Общие вопросы Delphi 1 18.02.2011 18:46
Could not convert variant of type (UnicodeString) into type (Double) postaveche БД в Delphi 11 13.12.2010 16:41