Spec-Zone.ru › GCC 8

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

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

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

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

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

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

Эта функция особенно полезна для создания «безопасных» определений макросов (так что каждый операнд вычисляется ровно один раз). Например, функция «maximum» обычно определяется как макрос в стандартном 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 (см. Тип).

В 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-8.5.0/gcc/Statement-Exprs.html

Spec-Zone.ru

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