Spec-Zone.ru › GCC 7

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

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

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

({ 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 (см. Тип).

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

Spec-Zone.ru

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