Do czego służy operator przecinka?

Co robi operator , W C?

Author: Lundin, 2008-09-09

8 answers

Wyrażenie:

(expression1,  expression2)

Najpierw zostanie obliczone wyrażenie1, następnie zostanie obliczone wyrażenie2 i zostanie zwrócona wartość wyrażenie2 dla całego wyrażenia.

 102
Author: lillq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 18:37:07

Widziałem najczęściej używane w while pętle:

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

Wykona operację, a następnie zrobi test na podstawie efektu ubocznego. Innym sposobem byłoby zrobić to tak:

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}
 89
Author: crashmstr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-29 05:13:13

Operator przecinka łączy dwa wyrażenia po obu jego stronach w jedno, oceniając je w kolejności od lewej do prawej. Wartość prawej strony jest zwracana jako wartość całego wyrażenia. (expr1, expr2) jest podobne do { expr1; expr2; }, ale możesz użyć wyniku expr2 w wywołaniu funkcji lub przypisaniu.

W pętlach for często widzi się inicjalizację lub utrzymywanie wielu zmiennych, takich jak:

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 */
}

Poza tym, użyłem go tylko "w Gniewie" w jednym innym przypadku, gdy podsumowując dwie operacje, które powinny zawsze iść razem w makro. Mieliśmy kod, który kopiował różne wartości binarne do bufora bajtowego do wysyłania w sieci, i wskaźnik utrzymywany tam, gdzie doszliśmy do:

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

Gdzie wartości były shortS lub int S zrobiliśmy to:

*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;

Później czytamy, że to nie było tak naprawdę poprawne C, ponieważ (short *)ptr nie jest już wartością l i nie może być zwiększana, chociaż nasz kompilator w tym czasie nie miał nic przeciwko. Aby to naprawić, dzielimy wyrażenie w dwóch:

*(short *)ptr = short_value;
ptr += sizeof(short);

Jednak to podejście polegało na tym, że wszyscy deweloperzy pamiętali o umieszczaniu obu deklaracji przez cały czas. Chcieliśmy funkcji, gdzie można przekazać w wskaźniku wyjściowym, wartość i typ wartości. To jest C, a nie c++ z szablonami, nie mogliśmy mieć funkcji przyjmującej dowolny typ, więc zdecydowaliśmy się na Makro:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

Używając operatora przecinka mogliśmy użyć tego w wyrażeniach lub jako instrukcji :

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));

Nie sugeruję, że któryś z tych przykładów to dobry styl! W rzeczy samej, wydaje mi się, że pamiętam kompletny kod Steve ' a McConnella zalecający nawet używanie operatorów przecinkowych w pętli for: dla czytelności i utrzymania pętli, pętla powinna być kontrolowana tylko przez jedną zmienną, a wyrażenia w samej linii for powinny zawierać tylko kod kontrolujący pętlę, a nie inne dodatkowe bity inicjalizacji lub utrzymania pętli.

 26
Author: Paul Stephenson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-15 19:32:41

Operator przecinka oceni lewy operand, odrzuci wynik, a następnie oceni prawy operand i będzie to wynik. Idiomatic jest używany, jak zaznaczono w linku, podczas inicjalizacji zmiennych używanych w pętli for i daje następujący przykład:

void rev(char *s, size_t len)
{
  char *first;
  for ( first = s, s += len - 1; s >= first; --s)
      /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
      putchar(*s);
}

W Przeciwnym Razie nie ma wielu wspaniałych zastosowańoperatora przecinka , chociaż łatwo jest nadużywać generować kod, który jest trudny do odczytania i utrzymać.

Z projektu standardu C99 gramatyka jest następująca:

expression:
  assignment-expression
  expression , assignment-expression

I paragraf 2 mówi:

Lewy operand operatora przecinka jest oceniany jako puste wyrażenie; {[31] } po jego ocenie znajduje się punkt sekwencji. Następnie jest obliczany parametr right; wynik ma swój typ i wartość. 97) jeśli zostanie podjęta próba modyfikacji wyniku operatora przecinka lub uzyskania do niego dostępu po następnej sekwencji punkt, zachowanie jest nieokreślone.

przypis 97 mówi:

Operator przecinka nie daje lvalue .

Co oznacza, że nie można przypisać do wyniku operatora przecinka.

Ważne jest, aby pamiętać, że operator przecinka ma najniższy priorytet i dlatego istnieją przypadki, w których użycie () może zrobić dużą różnicę, na przykład:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

Będzie miał następujące wyjście:

1 4
 24
Author: Shafik Yaghmour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

Powoduje ewaluację wielu poleceń, ale używa tylko ostatniego jako wartości wynikowej (rvalue, jak sądzę).

Więc...
int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

Powinno spowodować, że x zostanie ustawione na 8.

 8
Author: Ben Collins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 18:37:35

Operator przecinka nie robi nic sensownego, jest to w 100% zbędna funkcja. Głównym jego zastosowaniem jest "ludzie próbują być mądrzy" i dlatego używają go do (nieumyślnie) zaciemniania czytelnego kodu. Głównym obszarem zastosowania jest zaciemnianie pętli, na przykład:

for(int i=0, count=0; i<x; i++, count++)

Gdzie int i=0, count=0 w rzeczywistości nie jest operatorem przecinka, ale listą deklaracji(już tu jesteśmy zdezorientowani). {[5] } jest operatorem przecinka, który ocenia najpierw lewy, a następnie prawy. Wynik operator przecinka jest wynikiem właściwego operandu. Wynik lewego operandu jest odrzucany.

Ale powyższy kod mógłby być napisany w znacznie bardziej czytelny sposób bez operatora przecinka:

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

Jedynym prawdziwym zastosowaniem operatora przecinka, jakie widziałem, są sztuczne dyskusje na temat punktów sekwencji, ponieważ operator przecinka zawiera punkt sekwencji między oceną lewego i prawego operandu.

Więc jeśli masz jakiś niezdefiniowany kod zachowania jak to:

printf("%d %d", i++, i++);

W rzeczywistości można przekształcić to w tylko nieokreślone zachowanie (kolejność oceny parametrów funkcji), pisząc

printf("%d %d", (0,i++), (0,i++));

Pomiędzy każdą ewaluacją i++ jest teraz punkt sekwencji, więc przynajmniej program nie będzie ryzykował awarii i spalenia się dłużej, nawet jeśli kolejność ewaluacji parametrów funkcji pozostaje nieokreślona.

Oczywiście nikt nie napisze takiego kodu w rzeczywistych aplikacjach, jest on przydatny tylko w dyskusjach językowych o punkty sekwencji w języku C.

Operator przecinka jest zakazany przez MISRA-C:2004 i MISRA-c:2012 z uzasadnieniem, że tworzy mniej czytelny kod.

 5
Author: Lundin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-24 16:26:59

Jak stwierdzono we wcześniejszych odpowiedziach, ewaluuje wszystkie polecenia, ale używa ostatniego jako wartości wyrażenia. Osobiście uważam, że jest to przydatne tylko w wyrażeniach pętlowych:

for (tmp=0, i = MAX; i > 0; i--)
 3
Author: DGentry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-09 18:43:30

Jedyne miejsce, gdzie widziałem, że jest to przydatne jest, gdy piszesz funky pętli, gdzie chcesz zrobić wiele rzeczy w jednym z wyrażeń (prawdopodobnie wyrażenie init lub wyrażenie pętli. Coś w stylu:

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;
}

Przepraszam, jeśli są jakieś błędy składniowe lub jeśli zmieszałem coś, co nie jest ścisłe C. Nie twierdzę, że operator , jest dobrą formą, ale do tego można go użyć. W powyższym przypadku prawdopodobnie użyłbym pętli while, więc wiele wyrażeń na init i pętla byłaby bardziej oczywista. (I inicjalizowałbym i1 I i2 inline zamiast deklarować, a następnie inicjować.... bla bla bla.)

 2
Author: Owen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-04 01:27:03