Как работает оператор запятой - PullRequest
163 голосов
/ 10 сентября 2008

Как работает оператор запятой в C ++?

Например, если я сделаю:

a = b, c;  

В конечном итоге равен b или c?

(Да, я знаю, что это легко проверить - просто документально, чтобы кто-то быстро нашел ответ.)

Обновление: Этот вопрос выявил нюанс при использовании оператора запятой. Просто документировать это:

a = b, c;    // a is set to the value of b!

a = (b, c);  // a is set to the value of c!

Этот вопрос был фактически вдохновлен опечаткой в ​​коде. Что должно было быть

a = b;
c = d;

Превратился в

a = b,    //  <-  Note comma typo!
c = d;

Ответы [ 9 ]

124 голосов
/ 10 сентября 2008

Обратите внимание, что оператор запятой может быть перегружен в C ++. Таким образом, фактическое поведение может сильно отличаться от ожидаемого.

Например, Boost.Spirit довольно умело использует оператор запятой для реализации инициализаторов списка для таблиц символов. Таким образом, это делает возможным и значимым следующий синтаксис:

keywords = "and", "or", "not", "xor";

Обратите внимание, что из-за приоритета оператора код (намеренно!) Идентичен

(((keywords = "and"), "or"), "not"), "xor";

То есть первый вызванный оператор - keywords.operator =("and"), который возвращает прокси-объект, для которого вызываются остальные operator,:

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");
120 голосов
/ 22 сентября 2008

Оператор запятой имеет наименьший приоритет всех операторов C / C ++. Следовательно, это всегда последнее, связываемое с выражением, означающее следующее:

a = b, c;

эквивалентно:

(a = b), c;

Еще один интересный факт заключается в том, что оператор запятой вводит точку последовательности . Это означает, что выражение:

a+b, c(), d

гарантированно оценивает три подвыражения ( a + b , c () и d ) в порядке. Это важно, если у них есть побочные эффекты. Обычно компиляторам разрешается оценивать подвыражения в любом порядке, который они сочтут нужным; например, при вызове функции:

someFunc(arg1, arg2, arg3)

аргументы могут быть оценены в произвольном порядке. Обратите внимание, что запятые в вызове функции - это , а не операторы; они являются разделителями.

73 голосов
/ 10 сентября 2008

Это будет равно b.

Оператор запятой имеет более низкий приоритет, чем присваивание.

62 голосов
/ 05 октября 2013

Оператор запятой:

  • имеет самый низкий приоритет
  • левоассоциативен

Версия оператора запятой по умолчанию определена для всех типов (встроенных и пользовательских) и работает следующим образом: exprA , exprB:

  • exprA оценивается
  • результат exprA игнорируется
  • exprB оценивается
  • результат exprB возвращается как результат всего выражения

В большинстве операторов компилятору разрешается выбирать порядок выполнения, и даже требуется вообще пропустить выполнение, если это не влияет на конечный результат (например, false && foo() пропустит вызов foo). Это, однако, не относится к оператору запятой, и описанные выше шаги всегда будут выполняться *.

На практике оператор запятой по умолчанию работает почти так же, как точка с запятой. Разница в том, что два выражения, разделенные точкой с запятой, образуют два отдельных оператора, а разделение запятыми сохраняет все как одно выражение. Вот почему оператор запятой иногда используется в следующих сценариях:

  • C требует одного выражения , а не оператора. например в if( HERE )
  • Синтаксис C требует одного оператора, не более, например в инициализации for цикл for ( HERE ; ; )
  • Если вы хотите пропустить фигурные скобки и оставить единственное утверждение: if (foo) HERE ; (пожалуйста, не делайте этого, это действительно ужасно!)

Если оператор не является выражением, точку с запятой нельзя заменить запятой. Например, они запрещены:

  • (foo, if (foo) bar) (if не является выражением)
  • int x, int y (объявление переменной не является выражением)

В вашем случае у нас есть:

  • a=b, c;, эквивалентно a=b; c;, при условии, что a относится к типу, который не перегружает оператор запятой.
  • a = b, c = d; эквивалентно a=b; c=d;, при условии, что a относится к типу, который не перегружает оператор запятой.

Обратите внимание, что не каждая запятая на самом деле является оператором запятой. Некоторые запятые, которые имеют совершенно другое значение:

  • int a, b; --- список объявлений переменных разделен запятыми, но это не операторы запятых
  • int a=5, b=3; --- это также список объявлений переменных через запятую
  • foo(x,y) --- список аргументов через запятую. Фактически, x и y могут оцениваться в любом порядке!
  • FOO(x,y) --- список аргументов макроса, разделенных запятыми
  • foo<a,b> --- список аргументов шаблона через запятую
  • int foo(int a, int b) --- список параметров через запятую
  • Foo::Foo() : a(5), b(3) {} --- список инициализаторов через запятую в конструкторе класса

* Это не совсем так, если вы применяете оптимизацию. Если компилятор распознает, что определенный фрагмент кода не оказывает абсолютно никакого влияния на остальные, он удалит ненужные операторы.

Дополнительное чтение: http://en.wikipedia.org/wiki/Comma_operator

37 голосов
/ 15 сентября 2008

Значение a будет b, но значение , выражение будет c. То есть в

d = (a = b, c);

a будет равно b, а d будет равно c.

8 голосов
/ 10 сентября 2008
Значение

b будет присвоено a. С C

ничего не случится
2 голосов
/ 04 июля 2015

Да Оператор запятой имеет более низкий приоритет, чем оператор присваивания

#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d\n",i);
          return 0;
}

Выход: я = 3
Потому что оператор запятой всегда возвращает самое правое значение.
В случае оператора запятой с оператором присваивания:

 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d\n",i);
      return 0;
}

Ouput: я = 1
Как мы знаем, оператор запятой имеет более низкий приоритет, чем присваивание .....

2 голосов
/ 10 сентября 2008

Значение a будет равно b, так как оператор запятой имеет более низкий приоритет, чем оператор присваивания.

0 голосов
/ 07 апреля 2013

Перво-наперво: Запятая на самом деле не оператор, для компилятора это просто токен, который получает значение в контексте с другими токенами.

Что это значит и зачем?

Пример 1:

Чтобы понять разницу между значением одного и того же токена в другом контексте, рассмотрим следующий пример:

class Example {
   Foo<int, char*> ContentA;
}

Обычно новичок в C ++ думает, что это выражение может / могло бы сравнивать вещи, но оно абсолютно неверно, значение токенов <, > и , зависит от контекста использования.

Правильная интерпретация приведенного выше примера состоит в том, что он представляет собой шаблон.

Пример 2:

Когда мы пишем типичный цикл for с несколькими переменными инициализации и / или несколькими выражениями, которые должны выполняться после каждой итерации цикла, мы также используем запятую:

for(a=5,b=0;a<42;a++,b--)
   ...

Значение запятой зависит от контекста использования, здесь это контекст конструкции for.

Что на самом деле означает запятая в контексте?

Чтобы еще больше усложнить (как всегда в C ++), оператор запятой может сам быть перегружен (спасибо Konrad Rudolph за указание на это).

Чтобы вернуться к вопросу, код

a = b, c;

означает для компилятора что-то вроде

(a = b), c;

, поскольку приоритет токена / оператора = выше приоритета токена ,.

и это интерпретируется в контексте как

a = b;
c;

(обратите внимание, что интерпретация зависит от контекста, здесь она не является ни вызовом функции / метода, ни созданием шаблона.)

...