Что такое строгое правило псевдонимов?

Существует более элегантный способ решения этой проблемы, если вы используете backbonejs:


    initialize: function(){
        this.addListener_openWidget();
    }

    addListener_openWidget: function(){
        this.listenToOnce( this, 'openWidgetView', function(e){this.openWidget(e)} );
    },

    events: {
        'click #openWidgetLink' : function(e){this.trigger('openWidgetView',e)},
        'click #closeWidgetLink' : 'closeWidget'
    },

    openWidget: function( e ){
       //Do your stuff
    },

    closeWidget: function(){
       this.addListener_openWidget();
    }

747
задан Sombrero Chicken 15 October 2018 в 12:57
поделиться

4 ответа

Типичная ситуация Вы встречаетесь со строгими проблемами искажения, при накладывании структуры (как устройство/сетевое сообщение) на буфер размера слова системы (как указатель на uint32_t с или uint16_t с. Когда Вы накладываете структуру на такой буфер, или буфер на такую структуру через указатель, бросая Вас может легко нарушить строгие правила искажения.

Так в этом виде установки, если бы я хочу отправить сообщение во что-то, у меня должно было бы быть два несовместимых указателя, указывающие на тот же блок памяти. Я мог бы тогда наивно кодировать что-то вроде этого:

typedef struct Msg
{
    unsigned int a;
    unsigned int b;
} Msg;

void SendWord(uint32_t);

int main(void)
{
    // Get a 32-bit buffer from the system
    uint32_t* buff = malloc(sizeof(Msg));

    // Alias that buffer through message
    Msg* msg = (Msg*)(buff);

    // Send a bunch of messages    
    for (int i =0; i < 10; ++i)
    {
        msg->a = i;
        msg->b = i+1;
        SendWord(buff[0]);
        SendWord(buff[1]);   
    }
}

строгое правило искажения делает эту установку недопустимой: разыменование указателя, который искажает объект, который не имеет совместимый тип или один из других типов, позволенных к 2011 C 6,5 абзацев 7 <глоток> 1 , является неопределенным поведением. К сожалению, можно все еще кодировать этот путь, , возможно получают некоторые предупреждения, имеют его прекрасная компиляция, только для имения странного неожиданного поведения, когда Вы выполняете код.

(GCC кажется несколько непоследовательным в своей способности дать предупреждения искажения, иногда давая нам дружественное предупреждение и иногда нет.)

Для наблюдения, почему это поведение не определено мы должны думать о том, что строгое правило искажения покупает компилятор. В основном, с этим правилом, это не должно думать о вставке инструкций обновить содержание buff каждое выполнение цикла. Вместо этого при оптимизации, с некоторыми раздражающе добровольными предположениями об искажении, это может опустить те инструкции, загрузка buff[0] и buff[1] в ЦП регистрируется однажды, цикл выполняется, и ускорьте тело цикла. Прежде чем строгое искажение было представлено, компилятор должен был жить в состоянии паранойи, которую содержание buff могло изменить в любое время отовсюду кем-либо. Таким образом для получения дополнительного края производительности, и принимающий большинство людей не делают указателей игры слов типа, строгое правило искажения было представлено.

Имеют в виду, если Вы думаете, что пример изобретен, это могло бы даже произойти при передаче буфера другой функции, делающей посылание за Вами если вместо этого Вы имеете.

void SendMessage(uint32_t* buff, size_t size32)
{
    for (int i = 0; i < size32; ++i) 
    {
        SendWord(buff[i]);
    }
}

И переписал наш более ранний цикл для использования в своих интересах этой удобной функции

for (int i = 0; i < 10; ++i)
{
    msg->a = i;
    msg->b = i+1;
    SendMessage(buff, 2);
}

, компилятор может или не может быть в состоянии или достаточно умный, чтобы попытаться встроить SendMessage, и это может или не может решить загрузить или не загрузить любителя снова. Если SendMessage часть другого API, это компилируется отдельно, это, вероятно, имеет инструкции загрузить содержание любителя. С другой стороны возможно, Вы находитесь в C++, и это - некоторый шаблонный заголовок только реализация, что компилятор думает, что это может встроить. Или возможно это - просто что-то, что Вы записали в своем.c файле для Вашего собственного удобства. Так или иначе неопределенное поведение могло бы все еще последовать. Даже когда мы знаем часть из того, что происходит под капотом, это - все еще нарушение правила, таким образом, никакое четко определенное поведение не гарантируется. Таким образом, только путем обертывания в функции, которая берет наше слово, которому не обязательно помогает разграниченный буфер.

Поэтому, как я обхожу это?

  • Использование объединение. Большинство компиляторов поддерживает это, не жалуясь на строгое искажение. Это позволяется в C99 и явно позволяется в C11.

    union {
        Msg msg;
        unsigned int asBuffer[sizeof(Msg)/sizeof(unsigned int)];
    };
    
  • можно отключить строгое искажение в компиляторе ( f [нет-] строгое искажение в gcc))

  • , можно использовать char* для искажения вместо слова системы. Правила позволяют исключение для [1 112] (включая [1 113] и unsigned char). Всегда предполагается что char* псевдонимы другие типы. Однако это не будет работать другой путь: нет никакого предположения, что Ваша структура искажает буфер символов.

Новичок остерегается

, Это - только одно потенциальное минное поле при накладывании двух типов друг на друга. Необходимо также изучить [приблизительно 1 118] порядок байтов , выравнивание слов , и как заниматься проблемами выравнивания до [1 120] структуры упаковки правильно.

Сноска

<глоток> 1 типы, что 2011 C 6.5 7 позволяет lvalue доступу:

  • тип, совместимый с эффективным типом объекта,
  • квалифицированная версия типа, совместимого с эффективным типом объекта,
  • тип, который является или неподписанным типом со знаком, соответствующим эффективному типу объекта,
  • тип, который является или неподписанным типом со знаком, соответствующим квалифицированной версии эффективного типа объекта,
  • агрегат или тип объединения, который включает один из вышеупомянутых типов среди его участников (включая, рекурсивно, члена подсовокупного или содержавшего объединения), или
  • тип символов.
539
ответ дан 31 revs, 16 users 74% 15 October 2018 в 12:57
поделиться

Тип, каламбурящий через броски указателя (в противоположность использованию объединения), является главным примером повреждения строгого искажения.

16
ответ дан Chris Jester-Young 15 October 2018 в 12:57
поделиться

Строгое искажение не позволяет различные типы указателей тем же данным.

Эта статья должна помочь Вам понять проблему в полных деталях.

6
ответ дан godel9 15 October 2018 в 12:57
поделиться

Лучшее объяснение, которое я нашел, Mike Acton, Понимающее Строгое Искажение . Это сфокусировалось немного на разработке PS3, но это - в основном просто GCC.

От статьи:

"Строгое искажение является предположением, сделанным C (или C++) компилятор, то разыменование, которое указатели на объекты различных типов никогда не будут отсылать к той же ячейке памяти (т.е. искажать друг друга.)"

Так в основном, если Вы имеете int* указывающий на некоторую память, содержащую int и затем Вы указываете float* на ту память и используете ее в качестве float, Вы нарушаете правило. Если Ваш код не будет уважать это, то оптимизатор компилятора, скорее всего, повредит Ваш код.

исключение из правила char*, которому позволяют указать на любой тип.

228
ответ дан Palec 15 October 2018 в 12:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: