2015-09-29 3 views
3

Я думал, что я знал, C очень хорошо, но я запутался следующий код:Строгий сглаживание и гибкий член массива

typedef struct { 
    int type; 
} cmd_t; 

typedef struct { 
    int size; 
    char data[]; 
} pkt_t; 

int func(pkt_t *b) 
{ 
    int *typep; 
    char *ptr; 

    /* #1: Generates warning */ 
    typep = &((cmd_t*)(&(b->data[0])))->type; 

    /* #2: Doesn't generate warning */ 
    ptr = &b->data[0]; 
    typep = &((cmd_t*)ptr)->type; 

    return *typep; 
} 

Когда я компилирую с GCC, я получаю «разыменования типа каламбурил-указатель нарушить правила строгого сглаживания ".

  1. Почему я вообще получаю это предупреждение? Я разыменовываю в массиве char. Литье a char * на что-нибудь законное. Является ли это одним из тех случаев, когда массив не точно совпадает с указателем?

  2. Почему не оба назначения генерируют предупреждение? Второе задание эквивалентно первому, не так ли?

ответ

0

Когда строго сглаживание включено, компилятор позволил предположить, что два указателя различного типа (char* против cmt_t* в данном случае) не будет указывать на ту же ячейку памяти. Это позволяет использовать больший диапазон оптимизаций, которые в противном случае вы бы не хотели применять, если они действительно указывают на одно и то же место в памяти. Различные примеры/ужасные истории можно найти в этом question.

Именно поэтому, при строгом сглаживании, вы должны быть осторожны, как вы набираете караоке. Я считаю, что стандарт не допускает каких-либо действий, связанных с типом, что-то никогда (не цитируйте меня на этом), но большинство компиляторов имеют исключения для профсоюзов (мой google-fu не работает, чтобы открыть соответствующие страницы руководства) :

union float_to_int { 
    double d; 
    uint64_t i; 
}; 

union float_to_int ftoi; 
ftoi.d = 1.0; 
... = ftoi.i; 

к сожалению, это не совсем работа для вашей ситуации, как вы бы memcpy содержимого массива в союз, который является менее идеальным. Более простой подход состоял бы в том, чтобы просто отключить строгое сглаживание с помощью переключателя -fno-strict-aliasing. Это обеспечит правильность вашего кода, и вряд ли оно окажет существенное влияние на производительность (важно измерять эффективность работы).

Что касается того, почему предупреждение не появляется, когда линия разбита, я не знаю. Скорее всего, изменения в исходном коде позволяют запутать статический аналитический анализ компилятора, достаточный для того, чтобы он не видел тип-punning. Обратите внимание, что статический анализ, ответственный за обнаружение типа punning, не связан и не говорит о различных проходах оптимизации, которые предполагают строгое сглаживание. Вы можете придумать какой-либо статический анализ, сделанный компиляторами (если не указано иное) как наиболее подходящий тип вещей. Другими словами, отсутствие предупреждения не означает, что ошибок нет, а это означает, что простое разрывание линии не волшебным образом делает ваш тип караоке безопасным.

Смежные вопросы