Spec-Zone.ru › GCC 6

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

Spec-Zone.ru

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