Spec-Zone.ru › GCC 4

6.1 Операторы и объявления в выражениях

Составной оператор, заключенный в скобки, может использоваться в качестве выражения в GNU C. Это позволяет использовать циклы, переключатели и локальные переменные внутри выражения.

Напомним, что составной оператор — это последовательность операторов, окружённых фигурными скобками; в этом случае скобки окружают фигурные скобки. Например:

({ int y = foo (); int z;
   if (y > 0) z = y;
   else z = - y;
   z; })

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

Последним элементом в составном операторе должно быть выражение, за которым следует точка с запятой; значение этого подвыражения служит значением всего выражения. (Если вы используете другой тип оператора в конце фигурных скобок, выражение имеет тип void, и, следовательно, фактически не имеет значения.)

Эта функция особенно полезна для создания «безопасных» определений макросов (так что каждый операнд оценивается ровно один раз). Например, функция «максимум» обычно определяется как макрос в стандартном C следующим образом:

#define max(a,b) ((a) > (b) ? (a) : (b))

Но это определение вычисляет либо a, либо b дважды, что приведёт к плохим результатам, если операнд имеет побочные эффекты. В GNU C, если вы знаете тип операндов (здесь принятых как int), вы можете определить макрос безопасно следующим образом:

#define maxint(a,b) \
  ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

Встроенные операторы не допускаются в константных выражениях, таких как значение константы перечисления, ширина битового поля или начальное значение статической переменной.

Если вы не знаете тип операнда, вы всё равно можете это сделать, но должны использовать typeof или __auto_type (см. Typeof).

В G++, значение результата выражения оператора претерпевает разложение массивов и указателей на функции и возвращается по значению в окружающее выражение. Например, если A — это класс, то

A a;
     
({a;}).Foo ()

создаёт временный A объект для хранения результата выражения оператора, и этот объект используется для вызова Foo. Поэтому указатель this , наблюдаемый Foo , не является адресом a.

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

#define macro(a)  ({__typeof__(a) b = (a); b + 3; })
template<typename T> T function(T a) { T b = a; return b + 3; }

void foo ()
{
  macro (X ());
  function (X ());
}

имеет разные места, где уничтожаются временные объекты. Для случая macro , временный объект X уничтожается сразу после инициализации b. В случае function этот временный объект уничтожается при возвращении из функции.

Эти соображения означают, что использование выражений операторов такого вида в заголовочных файлах, предназначенных для работы с C++, вероятно, не является хорошей идеей. (Обратите внимание, что некоторые версии GNU C Library содержали заголовочные файлы, использующие выражения операторов, которые приводили к именно этой ошибке.)

Переход в выражение оператора с помощью goto или использование оператора switch за пределами выражения оператора с меткой case или default внутри выражения оператора запрещено. Переход в выражение оператора с вычисленной меткой goto (см. Метки как значения) приводит к неопределённому поведению. Выход из выражения оператора разрешён, но если выражение оператора является частью большего выражения, то не определено, какие другие подвыражения этого выражения были вычислены, за исключением случаев, когда описание языка требует вычисления определённых подвыражений до или после выражения оператора. В любом случае, как и при вызове функции, вычисление выражения оператора не переплетается с вычислением других частей содержащего выражения. Например,

foo (), (({ bar1 (); goto a; 0; }) + bar2 ()), baz();

вызывает foo и bar1 и не вызывает baz , но может или не может вызвать bar2. Если bar2 вызывается, то он вызывается после foo и перед bar1.

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gcc/Statement-Exprs.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API