C ++ 'da artırma - x ++ veya ++ x ne zaman kullanılır?


94

Şu anda C ++ öğreniyorum ve artışı bir süre önce öğrendim. Arttırmayı önce yapmak için "++ x" ve sonra yapmak için "x ++" kullanabileceğinizi biliyorum.

Yine de ikisini de ne zaman kullanacağımı gerçekten bilmiyorum ... "++ x" i hiç kullanmadım ve şimdiye kadar her şey yolunda gitti - peki, ne zaman kullanmalıyım?

Örnek: Bir for döngüsünde, "++ x" kullanmak ne zaman tercih edilir?

Ayrıca, birisi farklı artışların (veya azalmaların) nasıl çalıştığını tam olarak açıklayabilir mi? Gerçekten minnettar olurum.

Yanıtlar:


119

Bu bir tercih meselesi değil, mantık meselesi.

x++geçerli ifadeyi işledikten sonra değişken x'in değerini artırır .

++xgeçerli ifadeyi işlemeden önce x değişkeninin değerini artırır .

O yüzden sadece yazdığınız mantığa karar verin.

x += ++ii'yi artıracak ve x'e i + 1 ekleyecektir. x += i++i'yi x'e ekler, sonra i'yi artırır.


27
ve lütfen bir for döngüsünde, primatlarda kesinlikle hiçbir fark olmadığını unutmayın. Birçok kodlama stili, yanlış anlaşılabileceği durumlarda asla bir artış operatörü kullanılmamasını önerir; yani, x ++ veya ++ x yalnızca kendi satırında bulunmalıdır, asla y = x ++ olarak bulunmamalıdır. Şahsen, bunu sevmiyorum, ama bu çok nadir
Mikeage

2
Ve kendi satırında kullanılırsa, üretilen kodun hemen hemen aynı olacağı kesindir.
Nosredna

14
Bu bilgiçliği gibi görünebilir (bu :) başlıca nedeni), fakat C ++, olabilir x++değerine sahip bir rvalue olan xarttırmadan önce, x++değerine sahip bir lvalue olan xbir artış sonra. Her iki ifade de gerçek artan değerin x'e geri kaydedileceğini garanti etmez, yalnızca bir sonraki sıra noktasından önce gerçekleşeceği garanti edilir. Bazı ifadeler sıra noktalarına sahip olduğundan ve bazı ifadeler bileşik ifadeler olduğundan "geçerli ifadeyi işledikten sonra" tam olarak doğru değildir.
CB Bailey

10
Aslında cevap yanıltıcıdır. X değişkeninin değiştirildiği zaman noktası muhtemelen pratikte farklı değildir. Aradaki fark, x ++ 'nın önceki x değerinin bir r değerini döndürmek için tanımlanmış olması, ++ x ise x değişkenini ifade etmesidir.
sellibitze

5
@BeowulfOF: Cevap var olmayan bir emri ima ediyor. Standartta artışların ne zaman gerçekleştiğini söyleyecek hiçbir şey yoktur. Derleyici "x + = i ++" şu şekilde uygulama hakkına sahiptir: int j = i; i = i + 1; x + = j; "(yani 'i'," mevcut ifadeyi işlemeden "önce artırıldı). Bu nedenle" i = i ++ "tanımsız bir davranışa sahiptir ve bu yüzden cevabın" ince ayar yapılması "gerektiğini düşünüyorum." x'in açıklaması + = ++ i "herhangi bir sıralama önerisi olmadığı için doğrudur:" i'yi artıracak ve x'e i + 1 ekleyecek ".
Richard Corden

53

Scott Meyers , mantığın sonekin uygun olduğunu belirlediği durumlar dışında öneki tercih etmenizi söyler.

"Daha Etkili C ++" madde # 6 - bu benim için yeterli yetki.

Kitabın sahibi olmayanlar için, işte ilgili alıntılar. Sayfa 32'den itibaren:

Bir C programcısı olarak geçirdiğiniz günlerden, artırma operatörünün önek biçiminin bazen "artırma ve getirme" olarak adlandırıldığını, sonek biçiminin ise genellikle "getir ve artır" olarak bilindiğini hatırlayabilirsiniz. İki cümlenin hatırlanması önemlidir, çünkü hepsi resmi şartnameler olarak işlev görür ...

Ve 34. sayfada:

Verimlilik konusunda endişelenen biriyseniz, postfix artış işlevini ilk gördüğünüzde muhtemelen ter döktünüz. Bu işlevin dönüş değeri için geçici bir nesne yaratması gerekir ve yukarıdaki uygulama ayrıca oluşturulması ve yok edilmesi gereken açık bir geçici nesne yaratır. Önek artırma işlevinin böyle bir geçiciliği yoktur ...


4
Derleyici, artımdan önceki değerin rahatsız edici olmadığının farkına varmazsa, sonek artışını birkaç talimatta uygulayabilir - eski değeri kopyalayın ve ardından artırın. Önek artışı her zaman yalnızca bir talimat olmalıdır.
gnud

8
Bunu dün gcc ile test ettim: değerin çalıştırıldıktan sonra atıldığı bir for döngüsünde i++veya ++iüretilen kod aynı.
Giorgio

For döngüsünün dışında deneyin. Bir ödevdeki davranış farklı olmalıdır.
duffymo

Scott Meyers'e ikinci noktasında açıkça katılmıyorum - "x ++" veya "++ x" vakalarının% 90'ı veya daha fazlası tipik olarak herhangi bir atamadan izole edildiğinden ve optimize ediciler geçici değişkenlerin gerekmediğini anlayacak kadar akıllı olduğundan bu genellikle alakasızdır. bu gibi durumlarda oluşturulabilir. Bu durumda, iki form tamamen birbirinin yerine kullanılabilir. Bunun anlamı, "x ++" ile karıştırılmış eski kod tabanlarının yalnız bırakılması gerektiğidir - herhangi bir yerde performansı iyileştirmekten çok, bunları "++ x" olarak değiştiren ince hatalar ortaya koyma olasılığınız daha yüksektir. Muhtemelen "x ++" kullanmak ve insanları düşündürmek daha iyidir.
omatai

2
Scott Meyers'e istediğiniz kadar güvenebilirsiniz, ancak kodunuz performansa bağlıysa ++xve aralarındaki herhangi bir performans farkı x++gerçekten önemliyse, aslında ne olursa olsun her iki sürümü de tamamen ve doğru bir şekilde optimize edebilen bir derleyici kullanmanız çok daha önemlidir . bağlam. "Bu berbat eski çekici kullandığım için, sadece 43,7 derecelik bir açıyla çivi çakabilirim" bir ev inşa etmek için sadece 43,7 derece çivi çakmak için zayıf bir argüman. Daha iyi bir araç kullanın.
Andrew Henle

28

Gönderen cppreference yineleyicinızı artırılmıyor zaman:

Eski değeri kullanmayacaksanız, artırma öncesi operatörünü (++ iter) artım sonrası operatörüne (iter ++) tercih etmelisiniz. Arttırma sonrası genellikle şu şekilde uygulanır:

   Iter operator++(int)   {
     Iter tmp(*this); // store the old value in a temporary object
     ++*this;         // call pre-increment
     return tmp;      // return the old value   }

Açıkçası, ön artıştan daha az verimli.

Ön artış, geçici nesneyi oluşturmaz. Nesnenizin oluşturulması pahalıysa, bu önemli bir fark yaratabilir.


8

Anlambilimin (ön / sonun) önemli olmadığı pre / post artışını kullanırsanız, genleşmiş kodun aynı olduğunu fark etmek istiyorum.

misal:

pre.cpp:

#include <iostream>

int main()
{
  int i = 13;
  i++;
  for (; i < 42; i++)
    {
      std::cout << i << std::endl;
    }
}

post.cpp:

#include <iostream>

int main()
{

  int i = 13;
  ++i;
  for (; i < 42; ++i)
    {
      std::cout << i << std::endl;
    }
}

_

$> g++ -S pre.cpp
$> g++ -S post.cpp
$> diff pre.s post.s   
1c1
<   .file   "pre.cpp"
---
>   .file   "post.cpp"

5
Tam sayı gibi ilkel bir tür için, evet. A gibi bir şey için farkın ne olduğunu kontrol ettiniz std::map::iteratormi? Elbette iki operatör farklıdır, ancak sonuç kullanılmazsa derleyicinin postfix'i öneke optimize edip etmeyeceğini merak ediyorum. Postfix sürümünün yan etkiler içerebileceği düşünüldüğünde buna izin verildiğini sanmıyorum.
seh

Ayrıca, ' derleyici muhtemelen yan etkiye ihtiyaç duymadığınızı fark edecek ve onu ortadan kaldıracaktır ', daha karmaşık sonek operatörlerini herhangi bir sebep olmadan kullanan özensiz bir kod yazmak için bir bahane olmamalıdır. sözde öğretim materyalleri görünürde bir sebep olmadan postfix kullanır ve toptan satış çevresinde kopyalanır.
underscore_d

6

Akılda tutulması gereken en önemli şey, imo, x ++ 'nın, artış gerçekleşmeden önce değeri döndürmesi gerektiğidir - bu nedenle, nesnenin geçici bir kopyasını (ön artış) yapması gerekir. Bu, yerinde artırılan ve döndürülen ++ x'ten daha az etkilidir.

Yine de bahsetmeye değer başka bir şey, çoğu derleyicinin mümkün olduğunda bu tür gereksiz şeyleri optimize edebilmesidir, örneğin burada her iki seçenek de aynı koda yol açacaktır:

for (int i(0);i<10;++i)
for (int i(0);i<10;i++)

5

@ BeowulfOF ile aynı fikirdeyim, yine de açıklık için ifadeleri her zaman mantığın kesinlikle net olacak şekilde bölmeyi savunurdum, yani:

i++;
x += i;

veya

x += i;
i++;

Yani cevabım, eğer açık bir kod yazarsanız, o zaman bu nadiren önemli olacaktır (ve eğer önemliyse, o zaman kodunuz muhtemelen yeterince açık değildir).


2

Sadece ++ x'in x ++ 'dan daha hızlı olmasının beklendiğini tekrar vurgulamak istedim (özellikle x bazı rasgele türden bir nesneyse), bu nedenle mantıksal nedenlerle gerekmedikçe, ++ x kullanılmalıdır.


2
Sadece bu vurgunun muhtemelen yanıltıcı olduğunu vurgulamak istiyorum. İzole bir "x ++" ile biten bir döngüye bakıp "Aha! - bu kadar yavaş çalışmasının nedeni bu!" ve "++ x" olarak değiştirirsiniz, sonra kesinlikle hiçbir fark olmasını beklemeyin. İyileştiriciler, hiç kimse sonuçlarını kullanmayacakken geçici değişkenlerin yaratılmasına gerek olmadığını anlayacak kadar akıllıdır. Bunun anlamı, "x ++" ile delinmiş eski kod tabanlarının yalnız bırakılması gerektiğidir - herhangi bir yerde performansı artırmaktan çok, bunları değiştirerek hatalara yol açmanız daha olasıdır.
omatai

1

Farkı doğru söyledin. Bu, x'in her döngüden önce veya sonra artmasını isteyip istemediğinize bağlıdır. Program mantığınıza, neyin uygun olduğuna bağlıdır.

STL-Yineleyiciler (bu işleçleri de uygular) ile uğraşırken önemli bir fark, o ++ yineleyicinin işaret ettiği nesnenin bir kopyasını oluşturması, ardından artırması ve ardından kopyayı döndürmesidir. ++ Öte yandan, önce artışı yapar ve ardından yineleyicinin işaret ettiği nesneye bir başvuru döndürür. Bu, çoğunlukla performansın her bir parçası önemli olduğunda veya kendi STL-yineleyicinizi uyguladığınızda geçerlidir.

Düzenleme: önek ve sonek gösterimlerinin karışımı düzeltildi


Bir döngünün yinelemesinden "önce / sonra" hakkında konuşma, yalnızca koşulda ön / son artırma / azaltma meydana geldiğinde anlamlıdır. Daha sık olarak, herhangi bir mantığı değiştiremeyeceği devam cümlesinde olacaktır, ancak sınıf türlerinin postfix kullanması daha yavaş olabilir ve insanlar bunu sebepsiz kullanmamalıdır.
underscore_d

1

++ 'ın sonek formu, - işleci kullan-sonra-değiştir kuralını izler ,

Önek formu (++ x, - x), kural değişikliğinden sonra kullanılır .

Örnek 1:

Birden fazla değer, cout kullanılarak << ile basamaklandırıldığında, hesaplamalar (varsa) sağdan sola gerçekleşir ancak yazdırma işlemi soldan sağa, örneğin ( eğer başlangıçta 10 ise val ise)

 cout<< ++val<<" "<< val++<<" "<< val;

sonuçlanacak

12    10    10 

Örnek 2:

Turbo C ++'da, bir ifadede birden fazla ++ veya (herhangi bir biçimde) bulunursa, önce tüm önek formları hesaplanır, ardından ifade değerlendirilir ve son olarak sonek formları hesaplanır, örn.

int a=10,b;
b=a++ + ++a + ++a + a;
cout<<b<<a<<endl;

Turbo C ++ 'da çıktısı

48 13

Oysa günümüzün modern derleyicisinde çıktısı olacak (çünkü kurallara kesinlikle uyuyorlar)

45 13
  • Not: Bir ifadede aynı değişken üzerinde artırma / azaltma operatörlerinin çoklu kullanımı önerilmez. Bu tür
    ifadelerin işlenmesi / sonuçları derleyiciden derleyiciye değişir.

Bu, birden çok artırma / azaltma işlemi içeren ifadelerin "derleyiciden derleyiciye farklılık göstermesi" değil, daha da kötüsü: sıra noktaları arasındaki bu tür çoklu değişiklikler, tanımsız davranışa sahiptir ve programı zehirler.
underscore_d

0

Kodun netliği düşünüldüğünde dil sözdizimini anlamak önemlidir. Bir karakter dizesini kopyalamayı düşünün, örneğin artım sonrası:

char a[256] = "Hello world!";
char b[256];
int i = 0;
do {
  b[i] = a[i];
} while (a[i++]);

Döngünün, dizenin sonunda sıfır karakteriyle (yanlışı test eden) karşılaşarak yürütülmesini istiyoruz. Bu, ön artış değerinin test edilmesini ve ayrıca endeksin artırılmasını gerektirir. Ancak bu sırayla olmak zorunda değil - bunu ön artışla kodlamanın bir yolu şöyle olacaktır:

int i = -1;
do {
  ++i;
  b[i] = a[i];
} while (a[i]);

Bu daha net olan bir zevk meselesidir ve makinede bir dizi kayıt varsa, a [i] pahalı veya yan etkileri olan bir işlev olsa bile, her ikisinin de aynı yürütme süresine sahip olması gerekir. Endeksin çıkış değeri önemli bir fark olabilir.

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.