Что делает оператор запятой?
-
09-06-2019 - |
Вопрос
Что делает ,
оператор do в C?
Решение
Выражение:
(expression1, expression2)
Сначала вычисляется expression1, затем expression2, и значение expression2 возвращается для всего выражения.
Другие советы
Я видел, как большинство использовалось в while
петли:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Он выполнит операцию, затем проведет тест, основанный на побочном эффекте.Другим способом было бы сделать это следующим образом:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
Тот Самый оператор запятой вычислит левый операнд, отбросит результат, а затем вычислит правый операнд, и это будет результат.Тот Самый идиоматический используется, как указано в ссылке, при инициализации переменных, используемых в for
цикл, и это приводит к следующему примеру:
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
В остальном их не так уж много великий использование оператор запятой, хотя это и так легко злоупотреблять генерировать код, который трудно читать и поддерживать.
Из самого проект стандарта C99 грамматика выглядит следующим образом:
expression:
assignment-expression
expression , assignment-expression
и пункт 2 говорит:
Тот Самый левый операнд оператора запятой вычисляется как пустое выражение; после его оценки существует точка последовательности.Затем вычисляется правильный операнд;результат имеет свой тип и значение. 97) Если предпринимается попытка изменить результат оператора запятой или получить к нему доступ после следующей точки последовательности, поведение не определено.
Сноска 97 говорит:
Оператор запятой выполняет не дает значения lvalue.
это означает, что вы не можете присвоить результату оператор запятой.
Важно отметить, что оператор запятой имеет самый низкий приоритет и поэтому бывают случаи, когда использование ()
может иметь большое значение, например:
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
будет иметь следующий результат:
1 4
Оператор запятой объединяет два выражения по обе стороны от него в одно, вычисляя их оба в порядке слева направо.Значение в правой части возвращается как значение всего выражения. (expr1, expr2)
это похоже на { expr1; expr2; }
но вы можете использовать результат expr2
в вызове функции или назначении.
Это часто можно увидеть в for
циклы для инициализации или поддержания нескольких переменных, подобных этому:
for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
/* do something with low and high and put new values
in newlow and newhigh */
}
Помимо этого, я использовал его "в гневе" только в одном другом случае, когда завершал две операции, которые всегда должны выполняться вместе в макросе.У нас был код, который копировал различные двоичные значения в байтовый буфер для отправки по сети, и указатель сохранялся там, куда мы добрались:
unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;
*ptr++ = first_byte_value;
*ptr++ = second_byte_value;
send_buff(outbuff, (int)(ptr - outbuff));
Где были эти ценности short
ы или int
субъект: мы сделали это:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
Позже мы прочитали, что это был не совсем корректный C, потому что (short *)ptr
больше не является значением l и не может быть увеличено, хотя наш компилятор в то время не возражал.Чтобы исправить это, мы разделим выражение на две части:
*(short *)ptr = short_value;
ptr += sizeof(short);
Однако этот подход основывался на том, что все разработчики постоянно помнили о необходимости вводить оба оператора.Нам нужна была функция, в которую вы могли бы передавать выходной указатель, значение и тип значения.Поскольку это C, а не C ++ с шаблонами, мы не могли заставить функцию принимать произвольный тип, поэтому остановились на макросе:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Используя оператор запятой, мы смогли использовать это в выражениях или в виде инструкций по своему усмотрению:
if (need_to_output_short)
ASSIGN_INCR(ptr, short_value, short);
latest_pos = ASSIGN_INCR(ptr, int_value, int);
send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));
Я не утверждаю, что какой-либо из этих примеров является хорошим стилем!Действительно, я, кажется, помню слова Стива Макконнелла Код Завершен советую даже не использовать операторы с запятой в for
петля:для удобства чтения и сопровождения цикл должен управляться только одной переменной, а выражения в for
сама строка должна содержать только код управления циклом, а не другие дополнительные биты инициализации или обслуживания цикла.
Это вызывает вычисление нескольких операторов, но использует только последний из них в качестве результирующего значения (rvalue, я думаю).
Итак...
int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() );
должно привести к тому, что x будет установлено равным 8.
Оператор запятой не делает ничего значимого, это на 100% лишняя функция.Основное применение этого - "люди пытаются быть умными" и, следовательно, используют его для (непреднамеренного) запутывания читаемого кода.Основная область применения - это запутывание циклов for, например:
for(int i=0, count=0; i<x; i++, count++)
Где int i=0, count=0
на самом деле это не оператор запятой, а список объявлений (здесь мы уже запутались). i++, count++
является оператором запятой, который сначала вычисляет левый операнд, а затем правый операнд.Результат действия оператора запятой является результатом действия правого операнда.Результат использования левого операнда отбрасывается.
Но приведенный выше код мог бы быть написан гораздо более читабельным способом без оператора запятой:
int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
...
count++;
}
Единственное реальное использование оператора запятой, которое я видел, - это искусственные обсуждения точек последовательности, поскольку оператор запятой поставляется с точкой последовательности между вычислением левого и правого операндов.
Итак, если у вас есть какой-то неопределенный поведенческий код, подобный этому:
printf("%d %d", i++, i++);
На самом деле вы можете превратить это в просто неопределенное поведение (порядок вычисления параметров функции), написав
printf("%d %d", (0,i++), (0,i++));
Теперь существует точка последовательности между каждой оценкой i++
, так что, по крайней мере, программа больше не будет рисковать аварийным завершением работы и записью, даже несмотря на то, что порядок вычисления параметров функции остается неопределенным.
Конечно, никто не стал бы писать такой код в реальных приложениях, он полезен только для дискуссий языковых юристов о точках последовательности в языке Си.
Оператор запятой запрещен MISRA-C: 2004 и MISRA-C: 2012 с обоснованием того, что он создает менее читаемый код.
Как указывалось в предыдущих ответах, он вычисляет все операторы, но использует последний в качестве значения выражения.Лично я нашел это полезным только в выражениях цикла:
for (tmp=0, i = MAX; i > 0; i--)
Единственное место, где я видел, что это полезно, - это когда вы пишете сложный цикл, где вы хотите выполнить несколько действий в одном из выражений (вероятно, выражение init или выражение цикла.Что -то вроде:
bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
size_t i1, i2;
for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
{
if(a1[i1] != a2[i2])
{
return false;
}
}
return true;
}
Простите меня, если есть какие-либо синтаксические ошибки или если я перепутал что-либо, что не является строгим C.Я не утверждаю, что оператор , - это хорошая форма, но именно для этого вы могли бы его использовать.В приведенном выше случае я бы, вероятно, использовал while
вместо этого выполните цикл, чтобы множественные выражения в init и loop были более очевидными.(И я бы инициализировал i1 и i2 inline вместо объявления и последующей инициализации....бла-бла-бла.)
Я восстанавливаю это просто для того, чтобы ответить на вопросы от @Rajesh и @JeffMercado, которые, я думаю, очень важны, поскольку это один из лучших хитов поисковой системы.
Возьмем, к примеру, следующий фрагмент кода
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Он будет печатать
1 5
Тот Самый i
обращение рассматривается так, как описано в большинстве ответов.Все выражения вычисляются в порядке слева направо, но только последнее из них присваивается i
.В результате (
выражение )is
1`.
Тот Самый j
обращение следует другим правилам приоритета, поскольку ,
имеет наименьший приоритет оператора.Из-за этих правил компилятор видит присваивание-выражение, константа, constant ....Выражения снова вычисляются в порядке слева направо, и поэтому их побочные эффекты остаются видимыми, j
является 5
в результате j = 5
.
Интересно, int j = 5,4,3,2,1;
это не разрешено спецификацией языка.Ан инициализатор ожидает, что присваивание-выражение таким образом, прямой ,
оператор не допускается.
Надеюсь, это поможет.