Spec-Zone.ru › GCC 5

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.

Далее: Локальные метки, Наверх: Расширения C [Содержание][Индекс]

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

Spec-Zone.ru

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