Bir C ++ 'for' döngüsüne iki artış ifadesini nasıl koyarım?


93

İki değişkeni bir foryerine döngü koşulunda artırmak istiyorum .

Yani şöyle bir şey:

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

Bunun sözdizimi nedir?

Yanıtlar:


155

Yaygın bir deyim, her iki işleneni değerlendiren ve ikinci işleneni döndüren virgül operatörünü kullanmaktır . Böylece:

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

Ama gerçekten virgül operatörü mü?

Şimdi bunu yazmış olan bir yorumcu, bunun aslında bir virgül operatörü değil, for ifadesinde özel bir sözdizimsel şeker olduğunu öne sürdü. Bunu GCC'de şu şekilde kontrol ettim:

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

X'in a'nın orijinal değerini almasını bekliyordum, bu yüzden x için 5,6,7 .. görüntülemeliydi. Bende ne var

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

Bununla birlikte, ifadeyi ayrıştırıcıyı gerçekten virgül operatörü görmeye zorlayacak şekilde parantez içine alırsam, şunu elde ederim

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

Başlangıçta bunun virgül operatörü gibi davranmadığını gösterdiğini düşündüm, ancak ortaya çıktığı gibi, bu sadece bir öncelik sorunu - virgül operatörü mümkün olan en düşük önceliğe sahip , bu nedenle x = i ++, a ++ ifadesi etkili bir şekilde (x = i ++), a ++ olarak ayrıştırılır

Tüm yorumlar için teşekkürler, ilginç bir öğrenme deneyimiydi ve uzun yıllardır C kullanıyorum!


1
Bir for döngüsünün birinci veya üçüncü bölümündeki virgülün virgül operatörü olmadığını , yalnızca virgül ayırıcı olduğunu defalarca okudum . (Ben C ++ dilinin standardı ayrıştırma özellikle kötüyüm beri Ancak, ben, bunun için resmi bir kaynak bulmak için başarısız.)
Daniel Daranas

İlk önce hatalı olduğunu düşünmüştüm, ama bazı test kodu yazdım ve haklısın - virgül operatörü gibi davranmıyor. Cevabımı değiştirecek!
Paul Dixon

19
O ise bu bağlamda bir virgül operatörü. Beklediğiniz şeyi alamamanızın nedeni, komut operatörünün atama operatöründen daha düşük önceliğe sahip olmasıdır, bu nedenle parantezler olmadan (x = i ++), j ++ olarak ayrıştırılır.
caf

6
Virgül operatörüdür. Atama, virgül operatöründen daha güçlü bir şekilde bağlanır, bu nedenle x = i ++, a ++ ayrıştırılır (x = i ++), a ++ ve x = (i ++, a ++) değil. Bu özellik bazı kütüphaneler tarafından kötüye kullanılır, böylece v = 1,2,3; sezgisel şeyler yapar, ancak yalnızca v = 1, kendisine aşırı yüklenmiş virgül operatörünün eklediği bir proxy nesnesi döndürdüğü için.
AProgrammer

3
Tamam. Gönderen open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdf bölümünde 6.5.3 son bölüm bir "ifadesi" dir. (1.6 # 2 bir "ifade listesi" ni "virgülle ayrılmış ifadeler listesi" olarak tanımlasa da, bu yapı 6.5.3'te görünmez.). Bu, "++ i, ++ j" yazdığımızda bunun kendi başına bir ifade olması gerektiği ve dolayısıyla "," virgül operatörü (5.18) olması gerektiği anlamına gelir . (Bu, 5.18 # 2'nin dediği gibi, "virgülün özel bir anlam verildiği" örnekler olan "başlatıcıların listesi" veya "işlevler için bağımsız değişkenlerin listesi" değildir.). Yine de biraz kafa karıştırıcı buluyorum.
Daniel Daranas

56

Bunu dene

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

18
+1 Ayrıca ilk bölümde j olarak da belirtebilirsiniz. for (int i = 0, j = 0; i! = 5; ++ i, ++ j) {...}
Daniel Daranas

1
+1 Bir yan not olarak, aynı sözdizimi C # 'da çalışır (buraya "C # for loop incrament 2 counters" için bir Google aramasından geldim, bu yüzden bundan bahsetmeyi düşündüm).
CodingWithSpike

@CodingWithSpike: virgül C # içinde Well, olan özel bir virgül operatörü ifadesi orada görünmesi için, aslında yasal değil. C ++ 'da virgül operatörünün yasal kullanımı olan ancak C # tarafından reddedilen örnek:for( ; ; ((++i), (++j)) )
Ben Voigt

@BenVoigt'in virgülle ilgisi yok. Bu da yasal C # değildir: for(int i = 0; i != 5; (++i)) {Ekstra parantezler, derleyiciyi bunun artık bir "artış" işlemi olmadığını düşünmesi için kandırır.
CodingWithSpike

@CodingWithSpike: Bu doğru, ancak parantezler ayrıca C # 'nın virgülü görme şeklini değiştirir ve for action içindeki özel anlamı engeller.
Ben Voigt

6

Yapmamaya çalışın!

Gönderen http://www.research.att.com/~bs/JSF-AV-rules.pdf :

AV Kuralı 199
Bir for döngüsündeki artış ifadesi, tek bir döngü parametresini döngü için bir sonraki değere değiştirmek dışında hiçbir eylem gerçekleştirmez.

Gerekçe: Okunabilirlik.


4
Bu doğru, ancak dürüst olmak gerekirse, standart kurallar standardının bahçe tipi C (++) programı değil, bir savaş jetindeki gömülü yazılım için yazıldığından oldukça eminim. Bununla birlikte, muhtemelen içine girmek için iyi bir okunabilirlik alışkanlığıdır ve kim bilir, belki de F-35 yazılımı tasarlayacaksınız ve kırılması bir alışkanlık daha az olacak.
galois

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

2

Buraya, bir FOR döngüsünün artırma cümlesine ikinci bir indeksi nasıl kodlayacağımı hatırlatmak için geldim, bunu esas olarak başka bir projeye dahil ettiğim, C ++ ile yazılmış bir örnekte gözlemleyerek yapılabileceğini biliyordum.

Bugün, C # ile çalışıyorum, ancak bu konuda aynı kurallara uyacağından emindim, çünkü FOR ifadesi tüm programlamadaki en eski kontrol yapılarından biridir. Neyse ki, son zamanlarda eski C programlarından birinde bir FOR döngüsünün davranışını tam olarak belgelemek için birkaç gün geçirmiştim ve bu çalışmaların bugünün C # problemine, özellikle de ikinci indeks değişkeninin davranışına uygulanan dersler verdiğini çabucak fark ettim. .

Dikkatsiz olanlar için, gözlemlerimin bir özeti aşağıdadır. Bugün olan her şey, Locals penceresindeki değişkenleri dikkatlice gözlemleyerek, bir C # FOR ifadesinin tam olarak bir C veya C ++ FOR ifadesi gibi davrandığına dair beklentimi doğruladı.

  1. Bir FOR döngüsü ilk kez çalıştırıldığında, artış cümlesi (üçünün üçüncüsü) atlanır. Visual C ve C ++ 'da, artış, döngüyü uygulayan bloğun ortasında üç makine talimatı olarak üretilir, böylece ilk geçiş, başlatma kodunu yalnızca bir kez çalıştırır, ardından sonlandırma testini yürütmek için artış bloğunun üzerine atlar. Bu, bir FOR döngüsünün indeks ve limit değişkenlerinin durumuna bağlı olarak sıfır veya daha fazla kez çalıştırdığı özelliği uygular.
  2. Döngünün gövdesi yürütülürse, son ifadesi, ilk yineleme tarafından atlanan üç artırma talimatından ilkine bir atlamadır. Bunlar çalıştırıldıktan sonra, kontrol doğal olarak orta cümleyi uygulayan limit testi koduna düşer. Bu testin sonucu, FOR döngüsünün gövdesinin çalışıp çalışmayacağını veya denetimin kapsamının altındaki atlamayı geçtikten sonra sonraki talimata aktarılıp aktarılmayacağını belirler.
  3. Kontrol, FOR döngü bloğunun altından artış bloğuna aktarıldığından, indeks değişkeni test yürütülmeden önce artırılır. Bu davranış, sınır cümleciklerinizi neden öğrendiğiniz şekilde kodlamanız gerektiğini açıklamakla kalmaz, aynı zamanda virgül operatörü aracılığıyla eklediğiniz ikincil artımları da etkiler, çünkü üçüncü cümlenin parçası haline gelir. Bu nedenle, ilk yinelemede değiştirilmez, ancak gövdeyi hiçbir zaman yürütmeyen son yinelemede değişir.

Döngü sona erdiğinde dizin değişkenlerinizden herhangi biri kapsamda kalırsa, gerçek dizin değişkeni durumunda bunların değeri döngüyü durduran eşikten bir yüksek olacaktır. Benzer şekilde, örneğin, ikinci değişken döngü girilmeden önce sıfır olarak başlatılırsa, sondaki değeri, bir azalma değil, bir artış (++) olduğu ve içinde hiçbir şey olmadığı varsayılarak yineleme sayısı olacaktır. Döngünün gövdesi değerini değiştirir.


1

Squelart'a katılıyorum. İki değişkeni artırmak, özellikle bunlardan yalnızca birini test ediyorsanız hataya açıktır.

Bunu yapmanın okunaklı yolu şudur:

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

Fordöngüler, döngünüzün bir artan / azalan değişken üzerinde çalıştığı durumlar içindir. Diğer herhangi bir değişken için, döngüde değiştirin.

jBağlı olmanız gerekiyorsa i, neden orijinal değişkeni olduğu gibi bırakıp eklemeyesiniz i?

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

Mantığınız daha karmaşıksa (örneğin, aslında birden fazla değişkeni izlemeniz gerekiyorsa), bir whiledöngü kullanırım .


2
İlk örnekte, j, i'den bir kez daha artırılır! İlk x adım için bazı işlemlerin yapılması gereken bir yineleyiciye ne dersiniz? (Ve koleksiyon her zaman yeterince uzundur) Her yinelemede yineleyiciyi yükseltmekten daha fazlasını yapabilirsiniz, ancak çok daha temiz imho.
Peter Smit

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

1
Döngü yapmamanın ive ayerel olmanın anlamı nedir ?
sbi

2
Yok, sadece her iki artımın for'da nasıl yapılacağını gösteriyor, bu sadece bir sytnax örneği
Arkaitz Jimenez

0

Matematik kullanın. Eğer iki işlem matematiksel olarak döngü yinelemesine bağlıysa, neden matematik yapmayalım?

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

Veya daha spesifik olarak OP'nin örneğine atıfta bulunarak:

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

Özellikle bir işleve değere göre aktarıyorsanız, tam olarak istediğinizi yapan bir şey elde etmelisiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.