Bagaimana cara menempatkan dua pernyataan kenaikan dalam loop C ++ 'for'?

93

Saya ingin menambah dua variabel dalam a for kondisi -loop, bukan satu.

Jadi sesuatu seperti:

for (int i = 0; i != 5; ++i and ++j) 
    do_something(i, j);

Apa sintaks untuk ini?

Peter Smit
sumber

Jawaban:

154

Idiom umum adalah menggunakan operator koma yang mengevaluasi kedua operan, dan mengembalikan operan kedua. Jadi:

for(int i = 0; i != 5; ++i,++j) 
    do_something(i,j);

Tapi apakah itu benar-benar operator koma?

Sekarang setelah menulis itu, seorang pemberi komentar menyarankan itu sebenarnya adalah gula sintaksis khusus dalam pernyataan for, dan bukan operator koma sama sekali. Saya memeriksanya di GCC sebagai berikut:

int i=0;
int a=5;
int x=0;

for(i; i<5; x=i++,a++){
    printf("i=%d a=%d x=%d\n",i,a,x);
}

Saya mengharapkan x untuk mengambil nilai asli dari a, jadi seharusnya menampilkan 5,6,7 .. untuk x. Apa yang saya dapatkan adalah ini

i=0 a=5 x=0
i=1 a=6 x=0
i=2 a=7 x=1
i=3 a=8 x=2
i=4 a=9 x=3

Namun, jika saya mengurung ekspresi untuk memaksa pengurai benar-benar melihat operator koma, saya mengerti ini

int main(){
    int i=0;
    int a=5;
    int x=0;

    for(i=0; i<5; x=(i++,a++)){
        printf("i=%d a=%d x=%d\n",i,a,x);
    }
}

i=0 a=5 x=0
i=1 a=6 x=5
i=2 a=7 x=6
i=3 a=8 x=7
i=4 a=9 x=8

Awalnya saya pikir ini menunjukkan bahwa ini tidak berperilaku sebagai operator koma sama sekali, tetapi ternyata, ini hanyalah masalah prioritas - operator koma memiliki kemungkinan prioritas terendah , jadi ekspresi x = i ++, a ++ efektif diuraikan sebagai (x = i ++), a ++

Terima kasih atas semua komentarnya, itu adalah pengalaman belajar yang menarik, dan saya telah menggunakan C selama bertahun-tahun!

Paul Dixon
sumber
1
Saya telah membaca beberapa kali bahwa koma di bagian pertama atau ketiga dari perulangan for bukanlah operator koma, tetapi hanya pemisah koma. (Namun, saya gagal menemukan sumber resmi untuk ini, karena saya sangat buruk dalam mengurai standar bahasa C ++.)
Daniel Daranas
Saya pertama kali mengira Anda salah, tetapi saya menulis beberapa kode uji dan Anda benar - tidak berperilaku seperti operator koma. Akan mengubah jawaban saya!
Paul Dixon
19
Ini adalah operator koma dalam konteks itu. Alasan Anda tidak mendapatkan apa yang Anda harapkan adalah bahwa operator perintah memiliki prioritas yang lebih rendah daripada operator penugasan, jadi tanpa tanda kurung ia akan mengurai sebagai (x = i ++), j ++.
caf
6
Ini adalah operator koma. Tugas mengikat lebih kuat daripada operator koma, jadi x = i ++, a ++ diurai (x = i ++), a ++ dan bukan x = (i ++, a ++). Karakteristik tersebut disalahgunakan oleh beberapa perpustakaan sehingga v = 1,2,3; melakukan hal-hal intuitif, tetapi hanya karena v = 1 mengembalikan objek proxy yang ditambahkan oleh operator koma yang kelebihan beban.
Pemrogram
3
Baik. Dari open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdf bagian 6.5.3 bagian terakhir adalah "ekspresi". (Meskipun 1.6 # 2 mendefinisikan "daftar-ekspresi" sebagai "daftar ekspresi yang dipisahkan oleh koma", konstruksi ini tidak muncul di 6.5.3.). Ini berarti bahwa ketika kita menulis "++ i, ++ j" itu harus berupa ekspresi dalam dirinya sendiri, dan dengan demikian "," harus berupa operator koma (5.18). (Ini bukan "daftar penginisialisasi" atau "daftar argumen untuk fungsi", yang merupakan contoh di mana "koma diberi arti khusus", seperti yang dikatakan 5.18 # 2.). Saya merasa agak membingungkan.
Daniel Daranas
56

Coba ini

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);
yeyeyerman
sumber
18
+1 Anda juga bisa mendeklarasikan j di bagian pertama. untuk (int i = 0, j = 0; i! = 5; ++ i, ++ j) {...}
Daniel Daranas
1
+1 Sebagai catatan tambahan, sintaks yang sama ini bekerja di C # (Saya dapatkan di sini dari pencarian Google untuk "C # untuk penghitung loop incrament 2" jadi saya pikir saya akan menyebutkan ini).
CodingWithSpike
@CodingWithSpike: Nah, di C # koma itu khusus, sebenarnya tidak legal untuk ekspresi operator koma muncul di sana. Contoh penggunaan resmi operator koma di C ++, tetapi ditolak oleh C #:for( ; ; ((++i), (++j)) )
Ben Voigt
@BenVoigt tidak ada hubungannya dengan koma. Ini juga bukan C # legal: for(int i = 0; i != 5; (++i)) {Tanda kurung tambahan mengelabui compiler agar mengira ini bukan operasi "increment" lagi.
CodingWithSpike
@CodingWithSpike: Itu benar, tetapi tanda kurung juga mengubah cara C # melihat koma dan mencegah arti khusus di dalam for action.
Ben Voigt
6

Cobalah untuk tidak melakukannya!

Dari http://www.research.att.com/~bs/JSF-AV-rules.pdf :

Aturan AV 199
Ekspresi increment dalam loop for tidak akan melakukan tindakan selain mengubah parameter loop tunggal ke nilai loop berikutnya.

Rasional: Keterbacaan.

squelart
sumber
4
Itu benar, tapi sejujurnya saya cukup yakin bahwa standar aturan ditulis untuk perangkat lunak yang disematkan di jet tempur, bukan program varietas C (++) taman. Karena itu, mungkin kebiasaan membaca yang baik untuk dilakukan, dan siapa tahu, mungkin Anda akan merancang perangkat lunak F-35, dan kebiasaan itu akan berkurang.
galois
3
for (int i = 0; i != 5; ++i, ++j) 
    do_something(i, j);
Melayu
sumber
2

Saya datang ke sini untuk mengingatkan diri saya sendiri bagaimana membuat kode indeks kedua ke dalam klausa kenaikan dari loop FOR, yang saya tahu dapat dilakukan terutama dari mengamati dalam sampel yang saya masukkan ke dalam proyek lain, yang ditulis dalam C ++.

Hari ini, saya bekerja di C #, tetapi saya merasa yakin bahwa ia akan mematuhi aturan yang sama dalam hal ini, karena pernyataan FOR adalah salah satu struktur kontrol tertua di semua pemrograman. Untungnya, baru-baru ini saya menghabiskan beberapa hari dengan tepat mendokumentasikan perilaku loop FOR di salah satu program C saya yang lebih lama, dan saya segera menyadari bahwa studi tersebut berisi pelajaran yang diterapkan pada masalah C # saat ini, khususnya pada perilaku variabel indeks kedua. .

Bagi yang kurang waspada, berikut rangkuman pengamatan saya. Semua yang saya lihat terjadi hari ini, dengan mengamati variabel dengan cermat di jendela Lokal, menegaskan harapan saya bahwa pernyataan C # FOR berperilaku persis seperti pernyataan C atau C ++ FOR.

  1. Pertama kali loop FOR dijalankan, klausa increment (yang ketiga dari ketiganya) dilewati. Dalam Visual C dan C ++, increment dihasilkan sebagai tiga instruksi mesin di tengah blok yang mengimplementasikan loop, sehingga pass awal menjalankan kode inisialisasi hanya sekali, lalu melompati blok increment untuk menjalankan tes terminasi. Ini mengimplementasikan fitur yang dijalankan oleh loop FOR nol kali atau lebih, bergantung pada status variabel indeks dan batasnya.
  2. Jika badan perulangan dieksekusi, pernyataan terakhirnya adalah lompatan ke yang pertama dari tiga instruksi tambahan yang dilewati oleh iterasi pertama. Setelah eksekusi ini, kontrol secara alami masuk ke dalam kode uji batas yang mengimplementasikan klausa tengah. Hasil dari pengujian tersebut menentukan apakah badan loop FOR dieksekusi, atau apakah kontrol ditransfer ke instruksi berikutnya setelah lompatan di bagian bawah cakupannya.
  3. Sejak transfer kontrol dari bagian bawah blok loop FOR ke blok increment, variabel indeks bertambah sebelum pengujian dijalankan. Perilaku ini tidak hanya menjelaskan mengapa Anda harus membuat kode pada klausa limit seperti yang Anda pelajari, tetapi juga memengaruhi setiap kenaikan sekunder yang Anda tambahkan, melalui operator koma, karena ini menjadi bagian dari klausa ketiga. Karenanya, ini tidak diubah pada iterasi pertama, tetapi pada iterasi terakhir, yang tidak pernah mengeksekusi body.

Jika salah satu variabel indeks Anda tetap berada dalam cakupan saat pengulangan berakhir, nilainya akan menjadi satu lebih tinggi dari ambang batas yang menghentikan pengulangan, dalam kasus variabel indeks yang sebenarnya. Demikian juga, jika, misalnya, variabel kedua diinisialisasi ke nol sebelum pengulangan dimasukkan, nilainya di akhir adalah hitungan iterasi, dengan asumsi bahwa itu adalah kenaikan (++), bukan penurunan, dan tidak ada badan loop mengubah nilainya.

David A. Gray
sumber
1

Saya setuju dengan squelart. Menambahkan dua variabel rawan bug, terutama jika Anda hanya menguji salah satunya.

Ini adalah cara yang dapat dibaca untuk melakukan ini:

int j = 0;
for(int i = 0; i < 5; ++i) {
    do_something(i, j);
    ++j;
}

Forloop dimaksudkan untuk kasus di mana loop Anda berjalan pada satu variabel naik / turun. Untuk variabel lain, ubah di loop.

Jika Anda perlu jterikat i, mengapa tidak membiarkan variabel asli apa adanya dan menambahkan i?

for(int i = 0; i < 5; ++i) {
    do_something(i,a+i);
}

Jika logika Anda lebih kompleks (misalnya, Anda harus benar-benar memantau lebih dari satu variabel), saya akan menggunakan whileloop.

Ran Halprin
sumber
2
Dalam contoh pertama, j bertambah sekali lebih dari i! Bagaimana dengan iterator di mana perlu dilakukan beberapa tindakan untuk langkah x pertama? (Dan koleksinya selalu cukup lama) Anda bisa naik iterator setiap iterasi, tapi imho jauh lebih bersih.
Peter Smit
0
int main(){
    int i=0;
    int a=0;
    for(i;i<5;i++,a++){
        printf("%d %d\n",a,i);
    } 
}
Arkaitz Jimenez
sumber
1
Apa gunanya tidak membuat idan alokal ke loop?
sbi
2
Tidak ada, hanya menunjukkan bagaimana melakukan kedua kenaikan di untuk, itu hanya contoh dari sytnax
Arkaitz Jimenez
0

Gunakan Matematika. Jika dua operasi secara matematis bergantung pada perulangan, mengapa tidak dilakukan matematika?

int i, j;//That have some meaningful values in them?
for( int counter = 0; counter < count_max; ++counter )
    do_something (counter+i, counter+j);

Atau, lebih spesifik mengacu pada contoh OP:

for(int i = 0; i != 5; ++i)
    do_something(i, j+i);

Terutama jika Anda meneruskan ke fungsi berdasarkan nilai, maka Anda harus mendapatkan sesuatu yang melakukan apa yang Anda inginkan.

xaviersjs
sumber