Оценка/доступ к структуре

Рассмотрим две несколько разные версии одного и того же кода:

struct s
{
  int dummy[1];
};

volatile struct s s;

int main(void)
{
  s;
  return 0;
}

и

struct s
{
  int dummy[16];
};

volatile struct s s;

int main(void)
{
  s;
  return 0;
}

Вот что я получаю с gcc 4.6.2 для них:

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        call    ___main
        movl    _s, %eax
        xorl    %eax, %eax
        leave
        ret

        .comm   _s, 4, 2

и

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        call    ___main
        xorl    %eax, %eax
        leave
        ret

        .comm   _s, 64, 5

Обратите внимание на отсутствие доступа к s во втором случае.

Является ли это ошибкой компилятора или я просто имею дело со следующим утверждением стандарта C, и разработчики gcc просто выбрали такую ​​странную определенность для реализации и все еще играют по правилам?:

Что представляет собой доступ к объекту, который имеет тип с изменчивой квалификацией, определяется реализацией.

В чем причина этой разницы? Я бы, естественно, ожидал, что вся структура будет доступна (или не будет доступна, я не уверен), независимо от ее размера и того, что внутри него.

P.S. Что делает ваш компилятор (не-gcc или new gcc) в этом случае? (пожалуйста, ответьте на этот последний вопрос в комментарии, если это единственная часть, которую вы собираетесь адресовать, поскольку это не основной вопрос, который задают, но более интересный вопрос).

Ответ 1

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

лязг-3.4

При компиляции любого из этих фрагментов как С++, испускаемая сборка не ссылалась на s в любом случае. Фактически для обоих выпущено предупреждение:

volatile.c:8:2: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue] s;

Эти предупреждения не были выданы при компиляции в режиме C99. Как упоминалось в этом сообщении в блоге и эта запись в вики GCC из комментариев комментариев, используя s в этом контексте вызывает преобразование lvalue-to-rvalue в C, но не в С++. Это подтверждается рассмотрением Clang AST для C, так как существует ImplicitCastExpr из LvalueToRValue, который не существует в AST, сгенерированном с С++. (AST не зависит от размера структуры).

Быстрый grep источника Clang обнаруживает это при испускании агрегатных выражений:

case CK_LValueToRValue:
// If we're loading from a volatile type, force the destination
// into existence.
if (E->getSubExpr()->getType().isVolatileQualified()) {
  EnsureDest(E->getType());
  return Visit(E->getSubExpr());
}

EnsureDest заставляет испускать слот стека, размер и тип для выражения. Поскольку оптимизаторам не разрешено удалять изменчивые обращения, они остаются в виде скалярной нагрузки/хранилища и memcpy соответственно как в IR, так и в asm. Это поведение, которое я ожидал бы, учитывая приведенное выше.

GCC-4.8.2

Здесь я наблюдаю то же поведение, что и в вопросе. Однако, когда я изменяю выражение от s; до s.dummy;, доступ не отображается ни в одной из версий. Я не знаком с внутренними системами gcc, поскольку я с LLVM, поэтому не могу предположить, почему это произойдет. Но, основываясь на вышеупомянутых наблюдениях, я бы сказал, что это ошибка компилятора из-за несогласованности.