Время жизни составного литерала

6.5.2.5p5 говорит

Если составной литерал встречается вне тела функции, объект имеет статическую продолжительность хранения; в противном случае он имеет автоматическую продолжительность хранения, связанную с окружающим блоком.

Правильно ли я интерпретирую «вмещающий блок» здесь как «самый внутренний вмещающий блок»? (Потому что если он не самый внутренний, то какой?) Почему gcc и clang ведут себя так, как будто время жизни литералом была его объемлющая функция?

Пример:

long foo(long*);

void call_foo()
{
    {foo(&(long){42});}
    {foo(&(long){42});}
    {foo(&(long){42});}
    {foo(&(long){42});}
}

//for comparison

void call_foo2()
{
    {long x=42;foo(&x);}
    {long x=42;foo(&x);}
    {long x=42;foo(&x);}
    {long x=42;foo(&x);}
}

Код, сгенерированный gcc/clang в -O3:

call_foo:
  sub rsp, 40
  mov rdi, rsp
  mov QWORD PTR [rsp], 42
  call foo
  lea rdi, [rsp+8]
  mov QWORD PTR [rsp+8], 42
  call foo
  lea rdi, [rsp+16]
  mov QWORD PTR [rsp+16], 42
  call foo
  lea rdi, [rsp+24]
  mov QWORD PTR [rsp+24], 42
  call foo
  add rsp, 40
  ret
call_foo2:
  sub rsp, 24
  lea rdi, [rsp+8]
  mov QWORD PTR [rsp+8], 42
  call foo
  lea rdi, [rsp+8]
  mov QWORD PTR [rsp+8], 42
  call foo
  lea rdi, [rsp+8]
  mov QWORD PTR [rsp+8], 42
  call foo
  lea rdi, [rsp+8]
  mov QWORD PTR [rsp+8], 42
  call foo
  add rsp, 24
  ret

person PSkocik    schedule 07.12.2017    source источник
comment
Не могли бы вы уточнить, что вы думаете о коде, который вы показываете? Как вы думаете, в чем проблема? Почему это проблема? И вы спрашиваете только из любопытства, или есть еще какая-то причина, по которой вы спрашиваете? Какая фактическая проблема привела к этому сообщению?   -  person Some programmer dude    schedule 07.12.2017
comment
@Someprogrammerdude Этот составной литеральный код (который я хочу использовать, потому что он мне больше нравится), по-видимому, излишне тратит мое драгоценное пространство стека не на 1, а на 2 основных оптимизирующих компилятора, что меня беспокоит, поэтому я хочу знать, есть ли причина для Это.   -  person PSkocik    schedule 07.12.2017
comment
Составные литералы ведут себя так же, как и любые другие переменные — их область действия ограничена блоком { }, где они объявлены. Или, если они находятся вне функции, они имеют область действия файла и продолжительность статического хранения — как и любая другая переменная. Что касается того, почему вы получаете этот машинный код, я не знаю. Кажется действительно странным. Я ожидаю, что он будет повторно использовать одну и ту же область стека.   -  person Lundin    schedule 07.12.2017
comment
Подожди... г++? C++ не имеет составных литералов. Вы используете некоторое расширение gcc, которое может вести себя иначе, чем стандартные составные литералы C. Добавьте -std=c++11 -pedantic-errors.   -  person Lundin    schedule 07.12.2017
comment
@Lundin Compiler Explorer дает вам только g++, а не gcc, поэтому люди звонят с -x c, но этот вопрос касается только C, а не C++. (Практически локально сгенерированная программа, скомпилированная с помощью gcc, ведет себя в этом отношении так же, как и пример g++ -x c Compiler Explorer.)   -  person PSkocik    schedule 07.12.2017
comment
Локально (gcc/mingw с -O3) я получаю такое же неэффективное поведение для обеих функций, идентичный машинный код. Инструкция lea, за которой следует адрес, который изменяется на 0x11 для каждого вызова. Странно - я не могу этого объяснить. Может ли это быть из-за ASLR?   -  person Lundin    schedule 07.12.2017
comment
@Lundin: составные литералы вне функции не имеют области действия файла, потому что у них вообще нет области видимости. Идентификаторы имеют область видимости, то есть где в источнике они видны. Объекты имеют время жизни, то есть когда они существуют во время выполнения программы. Составной литерал не имеет идентификатора; нет строки символов, которая является его именем.   -  person Eric Postpischil    schedule 07.12.2017
comment
godbolt.org/g/K3ndVp, я пытаюсь с большим размером посмотреть, делают ли они что-то другое, но не . Мы не должны делать поспешных выводов об этом, может быть, есть веская причина. Кстати, ваш вопрос не очень ясен.   -  person Stargateur    schedule 07.12.2017
comment
@Lundin: составные литералы объявляются не больше, чем объявляются строковые литералы. Я не вижу никакого практического недостатка в том, чтобы составные литералы, адрес которых занят, вели себя так, как будто их время жизни продлевается, по крайней мере, до тех пор, пока код не покинет объемлющую функцию или пока они не будут повторно выполнены, в зависимости от того, что произойдет раньше, но я считаю их полезность строго ограниченной. если время жизни не может быть продлено за пределы ближайшего окружающего блока.   -  person supercat    schedule 16.12.2017


Ответы (2)


Кажется, для этого нет веских причин. Я бы назвал это ошибкой компилятора.

person R.. GitHub STOP HELPING ICE    schedule 07.12.2017

Рассмотрим код:

void whatever(void)
{
    THING *p;
    ...
    if (condition1)
      p=&(THING){...whatever...};
    ...
    doSomethingWith(p);
}

Согласно стандарту, составной литерал можно использовать только в том случае, если присваивание p выполняется одиночным несоставным оператором, управляемым if; изменение кода, чтобы if управляло составным оператором, потребовало бы значительной переработки:

void whatever(void)
{
    THING *p,temp_thing;
    ...
    if (condition1)
    {
      temp_thing = (THING){...whatever...};
      // Or else temp_thing.field1 = value1; temp_thing.field2=value2; etc.
      p=&temp_thing;
    }
    ...
    doSomethingWith(p);
}

Такое требование существенно и без необходимости подорвало бы полезность составных литералов (поскольку код можно было бы писать и без них). Гораздо более разумное правило указывало бы на то, что время жизни составного литерала продлевается до тех пор, пока код не покинет функцию, в которой он использовался, или выражение, создавшее его, не будет повторно выполнено, в зависимости от того, что произойдет раньше. Поскольку Стандарт позволяет компиляторам продлевать время жизни автоматических объектов, как они считают нужным, тот факт, что компилятор делает это, не следует считать ошибкой. С другой стороны, качественный компилятор, который намеренно будет более полезным, чем требует Стандарт, вероятно, должен явно документировать этот факт. В противном случае будущие сопровождающие могут заявить, что любые программы, которые полагаются на такое разумное поведение, являются «дефектными» и что компилятор может быть более «эффективным», если он перестанет их поддерживать.

person supercat    schedule 15.12.2017