Программирование ARM gcc: операторы и объявления в выражениях Sat, May 04 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

gcc: операторы и объявления в выражениях Печать
Добавил(а) microsin   

Составной оператор, заключенный в круглые скобки, может в GNU C работать как выражение (expression). Это позволяет в выражении использовать циклы, операторы 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; })

Обратите внимание, что введение объявлений переменных (как мы сделали в maxint) может вызвать затенение переменных, в то время как следующий пример с макросом max даст правильные результаты:

int _a = 1, _b = 2, c;
c = max (_a, _b);

Но это не так с макросом maxint:

int _a = 1, _b = 2, c;
c = maxint (_a, _b);

Эта проблема может возникнуть, когда используется рекурсия, наподобие:

#define maxint3(a, b, c) \
  ({int _a = (a), _b = (b), _c = (c); maxint (maxint (_a, _b), _c); })

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

Если вы не знаете тип операнда, то это все еще можно делать, но необходимо использовать typeof или __auto_type (см. описание typeof [2]).

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

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

.. то здесь конструируется временный объект A, который хранит результат выражения оператора, и который используется для вовлечения запуска Foo. Поэтому этот указатель, наблюдаемый 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 ());
}

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

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

Не допускаются перескоки с goto, или использование оператора switch вне выражения оператора с case или меткой default внутри выражения оператора. Перескок в выражение оператора с вычисленным goto (см. [3]) приведет к неопределенному поведению кода. Перескок из выражения оператора разрешается, однако если выражение оператора является частью большего выражения, то не указывается, какие субвыражения этого выражения были оценены, за исключением случаев, когда определение языка требует, чтобы определенные субвыражения были вычислены перед или после выражения оператора. Оператор break или continue внутри выражения оператора, используемого в цикле while, do или for, или оператора switch, или оператора init или increment переходит на внешний циклю или внешний switch, если он имеется (в противном случае это ошибка), а не к оператору цикла или switch, в чьем условии или выражении инициализации или приращения он появляется. В любом случае, как и при вызове функции, вычисление выражения оператора не перемежается с вычислением других частей содержащего его выражения. Например:

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

Здесь вызовы foo и bar1 не делают вызов baz, однако могут вызывать или не вызывать bar2. Если вызывается bar2, то это делается после foo и перед bar1.

[Ссылки]

1. Statements and Declarations in Expressions site:gcc.gnu.org.
2. gcc: использование typeof.
3. Labels as Values site:gcc.gnu.org.

 

Добавить комментарий


Защитный код
Обновить

Top of Page