Apa yang dilakukan oleh operator koma?


167

Apa yang dilakukan ,operator di C?



1
Seperti yang saya perhatikan dalam jawaban saya, ada titik urutan setelah evaluasi operan kiri. Ini tidak seperti koma dalam pemanggilan fungsi yang hanya gramatikal.
Shafik Yaghmour

2
@SergeyK. - Mengingat hal ini ditanyakan dan dijawab bertahun-tahun sebelum yang lain, kemungkinan besar yang lain merupakan duplikat dari pertanyaan ini. Namun, yang lain juga ditandai ganda dengan c dan c ++ , yang merupakan gangguan. Ini adalah tanya-jawab C-only, dengan jawaban yang layak.
Jonathan Leffler

Jawaban:


129

Ekspresi:

(expression1,  expression2)

Ekspresi1 pertama dievaluasi, kemudian ekspresi2 dievaluasi, dan nilai ekspresi2 dikembalikan untuk seluruh ekspresi.


3
maka jika saya menulis i = (5,4,3,2,1,0) maka idealnya harus mengembalikan 0, benar? tetapi saya diberi nilai 5? Bisakah Anda membantu saya memahami di mana saya salah?
Jayesh

19
@James: Nilai operasi koma akan selalu menjadi nilai dari ekspresi terakhir. Pada titik mana pun tidak akan imemiliki nilai 5, 4, 3, 2 atau 1. Ini hanya 0. Praktis tidak berguna kecuali ekspresi memiliki efek samping.
Jeff Mercado

6
Perhatikan bahwa ada titik urutan penuh antara evaluasi LHS dari ekspresi koma dan evaluasi RHS (lihat Shafik Yaghmour 's jawaban untuk kutipan dari standar C99). Ini adalah properti penting dari operator koma.
Jonathan Leffler

5
i = b, c;setara dengan (i = b), ckarena karena penugasan =memiliki prioritas lebih tinggi daripada operator koma ,. Operator koma memiliki prioritas terendah dari semua.
pesepeda

1
Saya khawatir bahwa tanda kurung menyesatkan dalam dua hal: (1) tidak perlu - operator koma tidak harus dikelilingi oleh tanda kurung; dan (2) mereka dapat dikacaukan dengan tanda kurung di sekitar daftar argumen pemanggilan fungsi - tetapi koma dalam daftar argumen bukan operator koma. Namun, memperbaikinya tidak sepenuhnya sepele. Mungkin: Dalam pernyataan: expression1, expression2;pertama expression1dievaluasi, mungkin karena efek sampingnya (seperti memanggil fungsi), kemudian ada titik urutan, kemudian expression2dievaluasi dan nilai dikembalikan ...
Jonathan Leffler

119

Saya telah melihat yang paling sering digunakan dalam whileloop:

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

Ini akan melakukan operasi, kemudian melakukan tes berdasarkan efek samping. Cara lain adalah dengan melakukannya seperti ini:

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

21
Hei, itu bagus! Saya sering harus melakukan hal-hal yang tidak lazim dalam satu lingkaran untuk memperbaiki masalah itu.
staticsan

6
Meskipun mungkin akan kurang jelas dan lebih mudah dibaca jika Anda melakukan sesuatu seperti: while (read_string(s) && s.len() > 5). Jelas itu tidak akan berhasil jika read_stringtidak memiliki nilai kembali (atau tidak memiliki nilai yang berarti). (Sunting: Maaf, tidak menyadari berapa usia kiriman ini.)
jamesdlin

11
@staticsan Jangan takut untuk digunakan while (1)dengan break;pernyataan di dalam tubuh. Mencoba memaksa bagian break-out dari kode menjadi test sementara atau turun ke tes do-while, seringkali merupakan pemborosan energi dan membuat kode lebih sulit untuk dipahami.
potrzebie

8
@jamesdlin ... dan orang masih membacanya. Jika Anda memiliki sesuatu yang berguna untuk dikatakan, maka katakanlah. Forum memiliki masalah dengan utas yang dibangkitkan karena utas biasanya diurutkan berdasarkan tanggal posting terakhir. StackOverflow tidak memiliki masalah seperti itu.
Dimitar Slavchev

3
@potrzebie Saya suka pendekatan koma jauh lebih baik daripada while(1)dan break;
Michael

38

The operator koma akan mengevaluasi operan kiri, membuang hasilnya dan kemudian mengevaluasi operan kanan dan yang akan menjadi hasilnya. Penggunaan idiomatis seperti tercantum dalam tautan adalah saat menginisialisasi variabel yang digunakan dalam satu forlingkaran, dan memberikan contoh berikut:

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

Kalau tidak, tidak ada banyak kegunaan besar dari operator koma , meskipun mudah untuk menyalahgunakan untuk menghasilkan kode yang sulit dibaca dan dipelihara.

Dari standar draft C99 tata bahasanya adalah sebagai berikut:

expression:
  assignment-expression
  expression , assignment-expression

dan ayat 2 mengatakan:

The operan kiri dari operator koma dievaluasi sebagai ekspresi kosong; ada titik urutan setelah evaluasi. Kemudian operan yang tepat dievaluasi; hasilnya memiliki jenis dan nilai. 97) Jika suatu upaya dilakukan untuk memodifikasi hasil dari operator koma atau untuk mengaksesnya setelah titik urutan berikutnya, perilaku tidak terdefinisi.

Catatan kaki 97 mengatakan:

Operator koma tidak menghasilkan nilai .

yang berarti Anda tidak dapat menetapkan hasil dari operator koma .

Penting untuk dicatat bahwa operator koma memiliki prioritas paling rendah dan oleh karena itu ada kasus di mana penggunaan ()dapat membuat perbedaan besar, misalnya:

#include <stdio.h>

int main()
{
    int x, y ;

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

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

akan memiliki output berikut:

1 4

28

Operator koma menggabungkan dua ekspresi kedua sisi menjadi satu, mengevaluasi keduanya dalam urutan kiri-ke-kanan. Nilai sisi kanan dikembalikan sebagai nilai keseluruhan ekspresi. (expr1, expr2)seperti { expr1; expr2; }tetapi Anda dapat menggunakan hasil expr2dalam panggilan fungsi atau tugas.

Sering terlihat dalam forloop untuk menginisialisasi atau mempertahankan beberapa variabel seperti ini:

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

Terlepas dari ini, saya hanya menggunakannya "dalam kemarahan" dalam satu kasus lain, ketika menyelesaikan dua operasi yang harus selalu berjalan bersama dalam makro. Kami memiliki kode yang menyalin berbagai nilai biner ke buffer byte untuk dikirim di jaringan, dan sebuah pointer mempertahankan di mana kami telah sampai:

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

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

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

Di mana nilainya shortatau intkami melakukan ini:

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

Kemudian kita membaca bahwa ini bukan C yang benar-benar valid, karena (short *)ptrtidak lagi bernilai l dan tidak dapat ditingkatkan, meskipun kompiler kami pada saat itu tidak keberatan. Untuk memperbaiki ini, kami membagi ekspresi menjadi dua:

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

Namun, pendekatan ini bergantung pada semua pengembang yang mengingat untuk menempatkan kedua pernyataan di sepanjang waktu. Kami menginginkan fungsi di mana Anda bisa meneruskan di pointer output, nilai dan dan tipe nilai. Ini adalah C, bukan C ++ dengan templat, kami tidak dapat memiliki fungsi mengambil tipe arbitrer, jadi kami memutuskan makro:

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

Dengan menggunakan operator koma, kami dapat menggunakan ini dalam ekspresi atau sebagai pernyataan seperti yang kami inginkan:

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

Saya tidak menyarankan contoh-contoh ini adalah gaya yang baik! Memang, saya sepertinya ingat Steve McConnell's Code Complete menasihati bahkan menggunakan operator koma dalam satu forloop: untuk keterbacaan dan pemeliharaan, loop harus dikontrol oleh hanya satu variabel, dan ekspresi dalam forbaris itu sendiri hanya boleh berisi kode loop-control, bukan bit tambahan lain dari inisialisasi atau pemeliharaan loop.


Terima kasih! Itu adalah jawaban pertama saya di StackOverflow: sejak itu saya mungkin telah belajar bahwa keringkasan harus dihargai :-).
Paul Stephenson

Kadang-kadang saya menghargai sedikit verbositas seperti halnya di sini di mana Anda menggambarkan evolusi solusi (bagaimana Anda sampai di sana).
green diod

8

Ini menyebabkan evaluasi beberapa pernyataan, tetapi hanya menggunakan yang terakhir sebagai nilai yang dihasilkan (nilai, saya pikir).

Begitu...

int f() { return 7; }
int g() { return 8; }

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

harus menghasilkan x yang diatur ke 8.


3

Seperti jawaban sebelumnya telah menyatakan itu mengevaluasi semua pernyataan tetapi menggunakan yang terakhir sebagai nilai ekspresi. Secara pribadi saya hanya menemukan itu berguna dalam ekspresi lingkaran:

for (tmp=0, i = MAX; i > 0; i--)

2

Satu-satunya tempat yang saya lihat bermanfaat adalah ketika Anda menulis loop funky di mana Anda ingin melakukan banyak hal dalam salah satu ekspresi (mungkin ekspresi init atau ekspresi loop. Sesuatu seperti:

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

Maafkan saya jika ada kesalahan sintaks atau jika saya mencampurkan sesuatu yang tidak ketat C. Saya tidak berargumen bahwa, operator adalah bentuk yang baik, tetapi untuk itulah Anda dapat menggunakannya. Dalam kasus di atas saya mungkin akan menggunakan whileloop sebagai gantinya sehingga beberapa ekspresi pada init dan loop akan lebih jelas. (Dan saya akan menginisialisasi i1 dan i2 inline alih-alih mendeklarasikan dan kemudian menginisialisasi .... bla bla bla.)


Saya kira maksud Anda i1 = 0, i2 = size -1
frankster

-2

Saya menghidupkan kembali ini hanya untuk menjawab pertanyaan dari @Rajesh dan @JeffMercado yang saya pikir sangat penting karena ini adalah salah satu hit mesin pencari teratas.

Ambil cuplikan kode berikut sebagai contoh

int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);

Itu akan mencetak

1 5

The ikasus ditangani seperti yang dijelaskan oleh sebagian besar jawaban. Semua ekspresi dievaluasi dalam urutan kiri-ke-kanan tetapi hanya yang terakhir ditugaskan i. Hasil dari ( ekspresi ) is1`.

Kasing jini mengikuti aturan prioritas yang berbeda karena ,memiliki prioritas operator terendah. Karena aturan-aturan, kompilator melihat tugas-ekspresi, konstanta, konstan ... . Ekspresi yang lagi dievaluasi dalam kiri-ke-kanan ketertiban dan efek samping mereka tetap terlihat, oleh karena itu, jadalah 5sebagai akibat darij = 5 .

Interstingly, int j = 5,4,3,2,1;tidak diizinkan oleh spec bahasa. Sebuah initializer mengharapkan tugas-ekspresi sehingga langsung ,operator tidak diperbolehkan.

Semoga ini membantu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.