Zastosowanie operatora przecinka C [duplikat]

To pytanie ma już odpowiedź tutaj:

Widzisz to w poleceniach for loop, ale wszędzie jest to legalna składnia. Jakie zastosowania znalazłeś dla niego gdzie indziej,jeśli w ogóle?

Author: Tiny Giant, 2009-10-23

20 answers

Język C (podobnie jak C++) jest historycznie mieszanką dwóch zupełnie różnych stylów programowania, które można określić jako "programowanie instrukcji" i "programowanie wyrażeń". Jak wiecie, każdy język programowania proceduralnego zwykle obsługuje takie podstawowe konstrukcje jak sekwencjonowanie i rozgałęzianie. Te podstawowe konstrukcje są obecne w językach C / C++ w dwóch formach: jedna do programowania instrukcji, druga do programowania wyrażeń.

Dla na przykład, gdy piszesz swój program w kategoriach instrukcji, możesz użyć sekwencji instrukcji oddzielonych ;. Kiedy chcesz zrobić rozgałęzienie, używaj if instrukcji. Można również korzystać z cykli i innych instrukcji transferu sterowania.

W programowaniu wyrażeń te same konstrukcje są również dostępne. To właśnie tutaj w grę wchodzi operator ,. Operator , w niczym innym jak separatorze wyrażeń sekwencyjnych w C, tzn. operator , w wyrażeniach programowanie pełni tę samą rolę co ; w programowaniu instrukcji. Rozgałęzianie w programowaniu wyrażeń odbywa się za pomocą operatora ?: oraz, alternatywnie, za pomocą właściwości oceny zwarciowej operatorów && i ||. (Programowanie wyrażeń nie ma jednak cykli. Aby zastąpić je rekurencją, trzeba by zastosować programowanie instrukcji.)

Na przykład następujący kod

a = rand();
++a;
b = rand();
c = a + b / 2;
if (a < c - 5)
  d = a;
else
  d = b;

Który jest przykładem tradycyjnego programowania instrukcji, może być zapisany ponownie w kategoriach programowania wyrażeń jako

a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? d = a : d = b;

Lub jako

a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;

Lub

d = (a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? a : b);

Lub

a = rand(), ++a, b = rand(), c = a + b / 2, (a < c - 5 && (d = a, 1)) || (d = b);

Nie trzeba dodawać, że w praktyce programowanie instrukcji zwykle wytwarza znacznie bardziej czytelny kod C / C++, więc zwykle używamy programowania wyrażeń w bardzo dobrze odmierzonych i ograniczonych ilościach. Ale w wielu przypadkach jest to przydatne. A granica między tym, co jest dopuszczalne, a tym, co nie, jest w dużej mierze kwestią osobistych preferencji i zdolności do rozpoznawać i czytać ustalone idiomy.

Jako dodatkowa uwaga: sama konstrukcja języka jest oczywiście dostosowana do stwierdzeń. Polecenia mogą swobodnie wywoływać wyrażenia, ale wyrażenia nie mogą wywoływać wyrażeń (poza wywołaniem predefiniowanych funkcji). Ta sytuacja jest dość interesująca w kompilatorze GCC, który obsługuje tak zwane "wyrażenia instrukcji" jako rozszerzenie (symetryczne do "wyrażeń wyrażeń" w standardowym C). "Oświadczenie expressions " pozwala użytkownikowi na bezpośrednie wstawianie kodu opartego na wyrażeniach do wyrażeń, tak jak może on wstawiać kod oparty na wyrażeniach do wyrażeń w standardowym C.

Jako kolejna uwaga dodatkowa: w języku C++ programowanie oparte na funkcjach odgrywa ważną rolę, która może być postrzegana jako inna forma "programowania wyrażeń". Zgodnie z aktualnymi trendami w projektowaniu C++, w wielu sytuacjach może być uważany za korzystniejszy od tradycyjnego programowania instrukcji.

 93
Author: AnT,
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-12-05 21:39:02

Myślę, że generalnie przecinek C nie jest dobrym stylem do użycia po prostu dlatego, że jest tak bardzo, bardzo łatwo go przegapić - albo przez kogoś innego, kto próbuje przeczytać/zrozumieć / naprawić Twój kod,albo sam miesiąc w dół linii. Poza deklaracjami zmiennych i oczywiście dla pętli, gdzie jest idiomatyczna.

Można go użyć na przykład do spakowania wielu instrukcji do operatora trójdzielnego (?:), ala:

int x = some_bool ? printf("WTF"), 5 : fprintf(stderr, "No, really, WTF"), 117;
Ale bogowie, dlaczego?!? (Widziałem to w ten sposób w prawdziwym kodzie, ale nie mają dostęp do niego, aby pokazać niestety)
 30
Author: Jack Lloyd,
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-10-23 13:00:14

Widziałem go w makrach, gdzie makro udaje funkcję i chce zwrócić wartość, ale najpierw musi wykonać inną pracę. Zawsze jest brzydki i często wygląda jak niebezpieczny hack.

Uproszczony przykład:

#define SomeMacro(A) ( DoWork(A), Permute(A) )

Tutaj B=SomeMacro(A) "zwraca" wynik Permute(A) i przypisuje go do "B".

 21
Author: Adisak,
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-10-23 14:00:01

Dwa zabójcze operatory przecinków w C++:

A) odczyt ze strumienia aż do napotkania określonego ciągu znaków (pomaga utrzymać kod suchy):

 while (cin >> str, str != "STOP") {
   //process str
 }

B) zapisanie złożonego kodu w konstruktorze inicjalizatorów:

class X : public A {
  X() : A( (global_function(), global_result) ) {};
};
 20
Author: P Shved,
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-10-24 22:28:20

Musiałem użyć przecinka do debugowania blokad mutex, aby umieścić wiadomość zanim blokada zacznie czekać.

Nie mogłem usunąć komunikatu logu w ciele pochodnego konstruktora blokady, więc musiałem umieścić go w argumentach konstruktora klasy bazowej używając : baseclass( ( log( "message" ) , actual_arg )) na liście inicjalizacyjnej. Zwróć uwagę na dodatkowy nawias.

Oto wyciąg z klas:

class NamedMutex : public boost::timed_mutex
{
public:
    ...

private:
    std::string name_ ;
};

void log( NamedMutex & ref__ , std::string const& name__ )
{
    LOG( name__ << " waits for " << ref__.name_ );
}

class NamedUniqueLock : public boost::unique_lock< NamedMutex >
{
public:

    NamedUniqueLock::NamedUniqueLock(
        NamedMutex & ref__ ,
        std::string const& name__ ,
        size_t const& nbmilliseconds )
    :
        boost::unique_lock< NamedMutex >( ( log( ref__ , name__ ) , ref__ ) ,
            boost::get_system_time() + boost::posix_time::milliseconds( nbmilliseconds ) ),
            ref_( ref__ ),
            name_( name__ )
    {
    }

  ....

};
 10
Author: fa.,
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-10-23 13:16:31

Biblioteka Boost Assignment jest dobrym przykładem przeciążenia operatora przecinka w użyteczny, czytelny sposób. Na przykład:

using namespace boost::assign;

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;
 10
Author: Stjepan Rajko,
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-10-23 17:49:44

Ze standardu C:

Lewy operand operatora przecinka jest oceniany jako puste wyrażenie; po jego ocenie znajduje się punkt sekwencji. Następnie jest obliczany właściwy operand; wynik ma swój typ i wartość. (Operator przecinka nie daje lvalue.)) Jeśli zostanie podjęta próba modyfikacji wyniku operatora przecinka lub uzyskania do niego dostępu po następnym punkcie sekwencji, zachowanie jest niezdefiniowane.

W skrócie pozwala określić więcej niż jedno wyrażenie, gdzie C oczekuje tylko jednego. Ale w praktyce jest używany głównie w pętlach for.

Zauważ, że:

int a, b, c;

Nie jest operatorem przecinka, jest listą deklaratorów.

 8
Author: Sophie Alpert,
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-10-26 02:27:13

Jest czasami używany w makrach, takich jak makra debugowania jak ten:

#define malloc(size) (printf("malloc(%d)\n", (int)(size)), malloc((size)))
[1]} (ale spójrz na {4]}tę straszną porażkę {5]}, przez Was naprawdę, bo co może się zdarzyć, gdy przesadzicie.)

Ale jeśli naprawdę tego nie potrzebujesz, lub jesteś pewien, że to sprawia, że kod jest bardziej czytelny i możliwy do utrzymania, polecam nie używać operatora przecinka.

 5
Author: Thomas Padron-McCarthy,
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:25:36

Możesz go przeciążyć(o ile to pytanie ma znacznik " C++"). Widziałem jakiś kod, w którym przeciążony przecinek był używany do generowania macierzy. Albo wektory, nie pamiętam dokładnie. Czyż to nie piękne (choć trochę mylące):

MyVector foo = 2, 3, 4, 5, 6;

 5
Author: SadSido,
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-10-23 13:10:13

Poza pętlą for, I nawet tam jest has może mieć aromat zapachu kodu, jedyne miejsce, które uznałem za dobre użycie operatora przecinka, to część delete:

 delete p, p = 0;

Jedyną wartością nad alternatywą jest to, że możesz przypadkowo skopiować / wkleić tylko połowę tej operacji, jeśli jest ona w dwóch liniach.

Podoba mi się też, bo jeśli zrobisz to z przyzwyczajenia, nigdy nie zapomnisz zerowego zadania. (Oczywiście, dlaczego p nie znajduje się wewnątrz jakiegoś wrappera auto_ptr, smart_ptr, shared_ptr, etc to inne pytanie.)
 5
Author: jmucchiello,
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-10-23 13:54:28

Biorąc pod uwagę cytat @ Nicolas Goy ze standardu, to brzmi jakbyś mógł napisać jednolinijkowy dla pętli typu:

int a, b, c;
for(a = 0, b = 10; c += 2*a+b, a <= b; a++, b--);
printf("%d", c);
Ale dobry Boże, człowieku, czy naprawdę chcesz, aby Twój kod C był bardziej niejasny w ten sposób?
 4
Author: PaulMcG,
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-10-23 13:02:51

Jest to bardzo przydatne w dodawaniu komentarza do makr ASSERT:

ASSERT(("This value must be true.", x));

Ponieważ większość makr typu assert wyświetla cały tekst swojego argumentu, dodaje to dodatkowy bit użytecznych informacji do twierdzenia.

 3
Author: Eclipse,
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-10-26 02:10:52

Ogólnie unikam używania operatora przecinka, ponieważ po prostu sprawia, że kod jest mniej czytelny. W prawie wszystkich przypadkach prostsze i jaśniejsze byłoby złożenie tylko dwóch oświadczeń. Like:

foo=bar*2, plugh=hoo+7;

Nie daje wyraźnej przewagi nad:

foo=bar*2;
plugh=hoo+7;

Jedyne miejsce poza pętlami, w którym go użyłem to if / else konstruuje, jak:

if (a==1)
... do something ...
else if (function_with_side_effects_including_setting_b(), b==2)
... do something that relies on the side effects ...

Możesz umieścić funkcję przed IF, ale jeśli jej uruchomienie zajmuje dużo czasu, możesz tego uniknąć, jeśli nie jest to konieczne i jeśli funkcja nie powinna być wykonywana, chyba że a!=1, to nie wchodzi w grę. Alternatywą jest zagnieżdżenie IF jako dodatkowej warstwy. To właśnie zwykle robię, ponieważ powyższy kod jest trochę tajemniczy. Ale robiłam to od czasu do czasu przecinkami, ponieważ zagnieżdżanie jest również tajemnicze.

 2
Author: Jay,
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-10-23 13:26:18

Często używam go do uruchamiania statycznej funkcji inicjującej w niektórych plikach cpp, aby uniknąć leniwych problemów z initalizacją klasycznych singletonów:

void* s_static_pointer = 0;

void init() {
    configureLib(); 
    s_static_pointer = calculateFancyStuff(x,y,z);
    regptr(s_static_pointer);
}

bool s_init = init(), true; // just run init() before anything else

Foo::Foo() {
  s_static_pointer->doStuff(); // works properly
}
 1
Author: Macke,
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-10-24 19:33:41

Dla mnie jedynym naprawdę użytecznym przypadkiem z przecinkami w C jest użycie ich do wykonania czegoś warunkowego.

  if (something) dothis(), dothat(), x++;

Jest to równoważne

  if (something) { dothis(); dothat(); x++; }

Tu nie chodzi o "mniej pisać", to po prostu wygląda bardzo jasno czasami.

Również pętle są takie same:

while(true) x++, y += 5;

Oczywiście obie mogą być użyteczne tylko wtedy, gdy część warunkowa lub część wykonywalna pętli jest dość mała, dwie-trzy operacje.

 1
Author: exebook,
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
2013-11-25 11:06:25

Jedyny raz, kiedy widziałem operator , używany poza pętlą for, to wykonanie assingment w trójstyku. To było dawno temu, więc nie mogę zapamiętać dokładnego stwierdzenia, ale było to coś w stylu:

int ans = isRunning() ? total += 10, newAnswer(total) : 0;

Oczywiście żadna osoba przy zdrowych zmysłach nie napisałaby takiego kodu, ale autor był złym geniuszem, który konstruuje instrukcje c w oparciu o wygenerowany przez siebie kod asemblera, a nie czytelność. Na przykład czasami używał pętli zamiast poleceń if, ponieważ preferował wygenerowany przez niego asembler.

Jego kod był bardzo szybki, ale nie do utrzymania, cieszę się, że nie muszę już z nim pracować.

 0
Author: iain,
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-10-23 13:34:41

Użyłem go do makra, aby "przypisać wartość dowolnego typu do bufora wyjściowego wskazywanego przez znak*, a następnie zwiększyć wskaźnik o wymaganą liczbę bajtów", tak jak to:

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

Użycie operatora przecinka oznacza, że makro może być używane w wyrażeniach lub jako instrukcje według życzenia:

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

Zmniejszyło to powtarzalne pisanie, ale musisz uważać, aby nie było zbyt nieczytelne.

Proszę zobaczyć moją zbyt długą wersję tej odpowiedzi tutaj .

 0
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
2017-05-23 12:17:37

Może być przydatny dla "code golf":

Kod: Playing Cubes

, W if(i>0)t=i,i=0; zapisuje dwa znaki.

 0
Author: Community,
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 11:46:45

Qemu ma kod, który używa operatora przecinka w części warunkowej pętli for (Zobacz QTAILQ_FOREACH_SAFE w QEMU-queue.h). To, co zrobili, sprowadza się do następujących rzeczy:

#include <stdio.h>

int main( int argc, char* argv[] ){
  int x = 0, y = 0;

  for( x = 0; x < 3 && (y = x+1,1); x = y ){
    printf( "%d, %d\n", x, y );
  }

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

  for( x = 0, y = x+1; x < 3; x = y, y = x+1 ){
    printf( "%d, %d\n", x, y );
  }

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

... z następującym wyjściem:

0, 1
1, 2
2, 3

3, 3

0, 1
1, 2
2, 3

3, 4

Pierwsza wersja tej pętli ma następujące efekty:

    Pozwala to uniknąć wykonywania dwóch zadań, więc szanse na to, że kod zostanie zsynchronizowany są zmniejszone.]}
  • ponieważ używa &&, przypisanie nie jest oceniane po ostatniej iteracji
  • ponieważ przypisanie nie jest oceniane, nie będzie próbowało odwoływać się do następnego elementu w kolejce, gdy znajduje się na końcu (w kodzie qemu, Nie w powyższym kodzie).
  • wewnątrz pętli masz dostęp do bieżącego i następnego elementu
 0
Author: Brian Vandenberg,
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
2012-03-28 17:47:41

Znaleziono go w inicjalizacji tablicy:

W C co dokładnie się stanie, jeśli użyję metody () do zainicjalizowania tablicy dwuwymiarowej zamiast {}?

Kiedy inicjalizuję tablicę a[][]:

int a[2][5]={(8,9,7,67,11),(7,8,9,199,89)};

A następnie wyświetla elementy tablicy.

Otrzymuję:

11 89 0 0 0 
0 0 0 0 0
 0
Author: S M,
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:25:36