Co robi ,
operator w C?
Co robi ,
operator w C?
Odpowiedzi:
Ekspresja:
(expression1, expression2)
Obliczane jest najpierw wyrażenie1, następnie wyrażenie2, a wartość wyrażenia2 jest zwracana dla całego wyrażenia.
i
miał wartości 5, 4, 3, 2 ani 1. Jest to po prostu 0. Jest praktycznie bezużyteczne, chyba że wyrażenia mają skutki uboczne.
i = b, c;
jest równoważne z (i = b), c
ponieważ przypisanie =
ma wyższy priorytet niż operator przecinka ,
. Operator przecinka ma najniższy priorytet ze wszystkich.
expression1, expression2;
najpierw expression1
jest oceniany, przypuszczalnie pod kątem skutków ubocznych (takich jak wywołanie funkcji), potem jest punkt sekwencji, następnie expression2
jest oceniany i zwracana wartość…
Widziałem najczęściej używane w while
pętlach:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Wykona operację, a następnie wykona test oparty na efekcie ubocznym. Innym sposobem byłoby zrobienie tego w ten sposób:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
while (read_string(s) && s.len() > 5)
. Oczywiście to nie zadziała, jeśli read_string
nie ma wartości zwracanej (lub nie ma sensownej wartości). (Edycja: Przepraszamy, nie zauważyłem, ile lat miał ten post.)
while (1)
z break;
oświadczeniem w treści. Próba wymuszenia przełamania fragmentu kodu w trakcie testu while lub w dół do testu do-while jest często stratą energii i sprawia, że kod jest trudniejszy do zrozumienia.
while(1)
i break
;
Operator przecinek oceni lewy argument, odrzucić wynik, a następnie ocenić prawy operand i że będzie wynik. Idiomatycznym zastosowanie, jak wspomniano w łączu przy inicjalizacji zmiennych stosowanych w for
pętlę 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 świetnych zastosowań operatora przecinka , chociaż łatwo jest nadużywać generowania kodu, który jest trudny do odczytania i utrzymania.
Z wersji roboczej standardu C99 gramatyka wygląda następująco:
expression:
assignment-expression
expression , assignment-expression
a ust. 2 mówi:
Lewy argument operatora przecinek oceniano jako pusta ekspresji; po jego ocenie znajduje się punkt sekwencji. Następnie oceniany jest prawy operand; wynik ma swój typ i wartość. 97) Jeśli zostanie podjęta próba zmodyfikowania wyniku operatora przecinka lub uzyskania do niego dostępu po następnym punkcie sekwencji, zachowanie jest niezdefiniowane.
Przypis 97 mówi:
Operator przecinka nie daje lwartości .
co oznacza, że nie możesz przypisać do wyniku operatora przecinka .
Należy zauważyć, że operator przecinka ma najniższy priorytet i dlatego istnieją przypadki, w których użycie ()
może spowodować 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ący wynik:
1 4
Operator przecinka łączy dwa wyrażenia po obu stronach w jedno, oceniając je w kolejności od lewej do prawej. Wartość po prawej stronie jest zwracana jako wartość całego wyrażenia.
(expr1, expr2)
jest jak, { expr1; expr2; }
ale możesz użyć wyniku expr2
w wywołaniu funkcji lub przypisaniu.
Często występuje w for
pętlach w celu zainicjowania lub utrzymania wielu zmiennych, jak poniżej:
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, kiedy pakowałem dwie operacje, które zawsze powinny iść razem w makro. Mieliśmy kod, który kopiował różne wartości binarne do bufora bajtów w celu wysłania w sieci, a wskaźnik utrzymywał się w miejscu, w którym 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));
Tam, gdzie wartości były short
s lub int
s, zrobiliśmy to:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
Później przeczytaliśmy, że nie było to naprawdę poprawne C, ponieważ (short *)ptr
nie jest już wartością l i nie może być zwiększane, chociaż nasz kompilator w tamtym czasie nie miał nic przeciwko. Aby to naprawić, podzieliliśmy wyrażenie na dwie części:
*(short *)ptr = short_value;
ptr += sizeof(short);
Jednak to podejście polegało na tym, że wszyscy deweloperzy pamiętali o umieszczaniu obu instrukcji przez cały czas. Chcieliśmy funkcji, w której można by przekazać wskaźnik wyjściowy, wartość i typ wartości. Ponieważ jest to C, a nie C ++ z szablonami, nie moglibyśmy mieć funkcji, która przyjmuje dowolny typ, więc zdecydowaliśmy się na makro:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Korzystając z operatora przecinka, mogliśmy użyć tego w wyrażeniach lub jako oświadczenia, jak chcieliśmy:
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órykolwiek z tych przykładów jest dobry! Rzeczywiście, wydaje mi się, że pamiętam Code Complete Steve'a McConnell'a, który odradzał nawet używanie operatorów przecinków w for
pętli: dla czytelności i łatwości utrzymania pętla powinna być kontrolowana tylko przez jedną zmienną, a wyrażenia w for
samej linii powinny zawierać tylko kod sterujący pętlą, a nie inne dodatkowe bity inicjalizacji lub utrzymania pętli.
Powoduje ocenę wielu instrukcji, ale wykorzystuje tylko ostatnią jako wartość wynikową (chyba rvalue).
Więc...
int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() );
powinno skutkować ustawieniem x na 8.
Jedynym miejscem, w którym widziałem, że jest przydatny, jest pisanie funkowej pętli, w której chcesz zrobić wiele rzeczy w jednym z wyrażeń (prawdopodobnie wyrażeniu init lub wyrażeniu pętli. Coś w rodzaju:
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 dodał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 while
pętli, aby wiele wyrażeń na init i loop było bardziej oczywiste. (I zainicjowałbym i1 i i2 inline zamiast deklarować, a następnie inicjalizować ... bla bla bla.)
Wracam do tego po prostu, aby odpowiedzieć na pytania @Rajesh i @JeffMercado, które moim zdaniem są bardzo ważne, ponieważ jest to jedno z najpopularniejszych haseł wyszukiwarek.
Weźmy na przykład następujący fragment kodu
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
To się wydrukuje
1 5
i
Przypadek jest traktowany jako wyjaśnione przez większość odpowiedzi. Wszystkie wyrażenia są obliczane w kolejności od lewej do prawej, ale tylko ostatnie jest przypisane i
. Wynik (
wyrażenia ) is
1`.
j
Przypadek następująco różne zasady pierwszeństwa, ponieważ ,
ma najniższy priorytet operatora. Z powodu tych przepisów, kompilator widzi przypisania ekspresję Constant Constant ... . Wyrażenia są ponownie oceniane w kolejności od lewej do prawej, a ich skutki uboczne pozostają widoczne, dlatego też j
jest 5
wynikiem j = 5
.
Co ciekawe, int j = 5,4,3,2,1;
specyfikacja języka nie zezwala. Inicjator oczekuje zadanie ekspresję więc bezpośredni ,
operator nie jest dozwolone.
Mam nadzieję że to pomoże.