C ++ 'da i ++ ve ++ i arasında bir performans farkı var mı?


352

Biz soru var arasında bir performans farkı yoktur i++ve ++i C ?

C ++ için cevap nedir?


Bu iki etiket, bu türden soruları bulmanın en kolay yolu olduğundan yeniden etiketledim. Ayrıca yapışkan etiketler olmayan başkalarına da baktım ve onlara yapışkan etiketler verdim.
George Stocker

104
C ++ ve ++ C kullanma arasında bir performans farkı var mı?
yeni123456

2
Makale: Yineleyiciler için postfix operatörü it ++ yerine ++ öneki artış operatörünü kullanmak mantıklı mı? - viva64.com/tr/b/0093

Yanıtlar:


426

[Yönetici Özeti: Kullanmak ++iiçin belirli bir nedeniniz yoksa kullanın i++.]

C ++ için, cevap biraz daha karmaşıktır.

Eğer ibasit bir türü (bir C ++ sınıfının bir örneğidir) 'dir, daha sonra C için verilen cevabı ( "Hayır hiçbir performans farkı yoktur") derleyici kodu üretme olduğundan, tutar.

Ancak, eğer ibir C ++ sınıfının bir örneğiyse, o zaman i++ve işlevlerden ++ibirine çağrı operator++yaparsınız. İşte bu işlevlerin standart bir çifti:

Foo& Foo::operator++()   // called for ++i
{
    this->data += 1;
    return *this;
}

Foo Foo::operator++(int ignored_dummy_value)   // called for i++
{
    Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
    ++(*this);
    return tmp;
}

Derleyici kod üretmediğinden, yalnızca bir operator++işlevi çağırdığından , tmpdeğişkeni ve ilişkili kopya yapıcısını optimize etmenin bir yolu yoktur . Kopya oluşturucu pahalıysa, bunun önemli bir performans etkisi olabilir.


3
Derleyicinin önleyebileceği şey, başka bir yorumda belirtildiği gibi, arayana tmp'yi NRVO aracılığıyla ayırarak tmp döndürmek için ikinci kopyadır.
Blaisorblade

7
Operatör ++ inline ise derleyici bundan tamamen kaçınamaz mı?
Eduard - Gabriel Munteanu

16
Evet, işleç ++ işaretlenirse ve tmp hiç kullanılmazsa, tmp nesnesinin yapıcısı veya yıkıcısının yan etkileri olmadıkça kaldırılabilir.
Zan Lynx

5
@kriss: C ve C ++ arasındaki fark, C'de operatörün satır içi olacağını ve bu noktada iyi bir optimize edicinin farkı kaldırabileceğini garanti etmenizdir; bunun yerine C ++ ile her zaman değil, satır içi kabul edemezsiniz.
Blaisorblade

3
Cevabın, kopya oluşturucunun derin kopyalar gerçekleştirdiği dinamik olarak ayrılmış (yığın) belleğe işaretçiler (otomatik, akıllı veya ilkel) içeren sınıflarla ilgili bir şeyden bahsetmiş olsaydım +1 olur. Bu gibi durumlarda, argüman yoktur, ++ i belki i ++ 'dan daha etkili bir büyüklük sırasıdır. Anahtarlar, artım sonrası anlambiliminiz algoritmanız tarafından gerçekten gerekli olmadığında, ön artış kullanma alışkanlığına sahip olmaktır ve daha sonra, doğası gereği, derleyiciniz optimize edebilirsiniz.
phonetagger

64

Evet. Var.

++ operatörü bir işlev olarak tanımlanabilir veya tanımlanmayabilir. İlkel tipler (int, double, ...) için operatörler yerleşiktir, bu nedenle derleyici muhtemelen kodunuzu optimize edebilir. Ancak ++ işlecini tanımlayan bir nesne söz konusu olduğunda işler farklıdır.

Operator ++ (int) işlevi bir kopya oluşturmalıdır. Bunun nedeni, postfix ++ 'ın sahip olduğundan farklı bir değer döndürmesi beklenir: değerini geçici bir değişkende tutmalı, değerini artırmalı ve temp değerini döndürmelidir. ++ (), ++ öneki durumunda, bir kopya oluşturmaya gerek yoktur: nesne kendisini artırabilir ve daha sonra kendisini geri döndürebilir.

İşte konunun bir örneği:

struct C
{
    C& operator++();      // prefix
    C  operator++(int);   // postfix

private:

    int i_;
};

C& C::operator++()
{
    ++i_;
    return *this;   // self, no copy created
}

C C::operator++(int ignored_dummy_value)
{
    C t(*this);
    ++(*this);
    return t;   // return a copy
}

Operator ++ (int) öğesini her aradığınızda bir kopya oluşturmanız gerekir ve derleyici bu konuda hiçbir şey yapamaz. Seçim yapıldığında, ++ () operatörünü kullanın; bu şekilde bir kopyasını kaydetmezsiniz. Birçok artış (büyük döngü?) Ve / veya büyük nesneler söz konusu olduğunda önemli olabilir.


2
"Ön artış operatörü, koda bir veri bağımlılığı getirir: CPU, değerin ifadede kullanılabilmesi için önce arttırma işleminin tamamlanmasını beklemelidir. artım sonrası operatörü için. " ( Oyun Motoru Mimarisi (2. baskı) ) Dolayısıyla, bir artım artışının kopyası hesaplama açısından yoğun değilse, yine de ön artışı yenebilir.
Matthias

Postfix kodunda, bu nasıl çalışır C t(*this); ++(*this); return t;İkinci satırda, bu işaretçiyi doğru artırıyorsunuz, bu yüzden bunu tartırıyorsanız nasıl güncellenir. Bunun değerleri önceden kopyalanmadı tmı?
rasen58

The operator++(int) function must create a copy.hayır öyle değil. Daha fazla kopya yokoperator++()
Severin Pappadeux

47

Artış operatörlerinin farklı çeviri birimlerinde olduğu durum için bir kıstas. G ++ 4.5 ile derleyici.

Şimdilik stil sorunlarını yoksay

// a.cc
#include <ctime>
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};

int main () {
    Something s;

    for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
    std::clock_t a = clock();
    for (int i=0; i<1024*1024*30; ++i) ++s;
    a = clock() - a;

    for (int i=0; i<1024*1024*30; ++i) s++; // warm up
    std::clock_t b = clock();
    for (int i=0; i<1024*1024*30; ++i) s++;
    b = clock() - b;

    std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
              << ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
    return 0;
}

O (n) artış

Ölçek

// b.cc
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};


Something& Something::operator++()
{
    for (auto it=data.begin(), end=data.end(); it!=end; ++it)
        ++*it;
    return *this;
}

Something Something::operator++(int)
{
    Something ret = *this;
    ++*this;
    return ret;
}

Sonuçlar

Sanal makinede g ++ 4.5 ile sonuçlar (zamanlama saniye cinsindendir):

Flags (--std=c++0x)       ++i   i++
-DPACKET_SIZE=50 -O1      1.70  2.39
-DPACKET_SIZE=50 -O3      0.59  1.00
-DPACKET_SIZE=500 -O1    10.51 13.28
-DPACKET_SIZE=500 -O3     4.28  6.82

O (1) artış

Ölçek

Şimdi aşağıdaki dosyayı alalım:

// c.cc
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};


Something& Something::operator++()
{
    return *this;
}

Something Something::operator++(int)
{
    Something ret = *this;
    ++*this;
    return ret;
}

Artımlılıkta hiçbir şey yapmaz. Bu, artımın sürekli karmaşıklığa sahip olduğu durumu simüle eder.

Sonuçlar

Sonuçlar artık çok değişken:

Flags (--std=c++0x)       ++i   i++
-DPACKET_SIZE=50 -O1      0.05   0.74
-DPACKET_SIZE=50 -O3      0.08   0.97
-DPACKET_SIZE=500 -O1     0.05   2.79
-DPACKET_SIZE=500 -O3     0.08   2.18
-DPACKET_SIZE=5000 -O3    0.07  21.90

Sonuç

Performans açısından

Önceki değere ihtiyacınız yoksa, ön artışı kullanmayı alışkanlık haline getirin. Yerleşik tiplerle bile tutarlı olun, buna alışacaksınız ve yerleşik bir türü özel bir türle değiştirirseniz gereksiz performans kaybı yaşama riskiyle karşılaşmazsınız.

Semantik-bilge

  • i++diyor increment i, I am interested in the previous value, though.
  • ++idiyor increment i, I am interested in the current valueya da increment i, no interest in the previous value. Yine, şimdi olmasanız bile buna alışacaksınız.

Knuth.

Erken optimizasyon tüm kötülüklerin köküdür. Erken kötüleşme gibi.


1
İlginç bir test. Şimdi, neredeyse iki buçuk yıl sonra, gcc 4.9 ve Clang 3.4 benzer bir eğilim gösteriyor. Clang her ikisiyle de biraz daha hızlı, ancak ön ve son düzeltme arasındaki eşitsizlik gcc'den daha kötü.
çorap çiğnemek

Gerçekten görmek istediğim, ++ i / i ++ 'ın fark yarattığı gerçek dünya örneğidir. Örneğin, std yineleyicilerden herhangi birinde bir fark yaratıyor mu?
Jakob Schou Jensen

@ JakobSchouJensen: Bunlar gerçek dünya örnekleri olarak düşünülmüştü. Karmaşık ağaç yapıları (örneğin kd ağaçları, dört ağaçlar) veya ifade şablonlarında kullanılan büyük kaplarla (SIMD donanımındaki veri akışını en üst düzeye çıkarmak için) büyük bir uygulama düşünün. Orada bir fark yaratırsa, anlamsal olarak gerekli değilse, neden belirli durumlar için artışın ardından geri döneceğinden emin değilim.
Sebastian Mach

@phresnel: Operatör ++ uygulamasının her gün bir ifade şablonu olduğunu düşünmüyorum - bunun gerçek bir örneği var mı? ++ işlecinin tipik kullanımı tamsayılar ve yineleyiciler üzerindedir. Bu, herhangi bir fark olup olmadığını bilmek ilginç olacağını düşünüyorum (elbette tamsayılarda fark yok - yineleyiciler).
Jakob Schou Jensen

@ JakobSchouJensen: Gerçek bir iş örneği yok, ancak bazı şeyleri saydığınız bazı çıtırtı uygulamaları. Wrt yineleyiciler, idiyomatik C ++ tarzında yazılmış bir ışın izleyiciyi düşünün for (it=nearest(ray.origin); it!=end(); ++it) { if (auto i = intersect(ray, *it)) return i; }ve gerçek ağaç yapısını (BSP, kd, Quadtree, Octree Grid, vb.) Böyle bir yineleyici bazı devlet örn korumak gerekir parent node, child node, indexböyle falan. Sonuçta, duruşum, sadece birkaç örnek olsa bile, ...
Sebastian Mach

20

Derleyicinin postfix durumunda geçici değişken kopyayı optimize edemeyeceğini söylemek tamamen doğru değildir. VC ile hızlı bir test, en azından bazı durumlarda bunu yapabileceğini gösterir.

Aşağıdaki örnekte, oluşturulan kod, örneğin önek ve postfix için aynıdır:

#include <stdio.h>

class Foo
{
public:

    Foo() { myData=0; }
    Foo(const Foo &rhs) { myData=rhs.myData; }

    const Foo& operator++()
    {
        this->myData++;
        return *this;
    }

    const Foo operator++(int)
    {
        Foo tmp(*this);
        this->myData++;
        return tmp;
    }

    int GetData() { return myData; }

private:

    int myData;
};

int main(int argc, char* argv[])
{
    Foo testFoo;

    int count;
    printf("Enter loop count: ");
    scanf("%d", &count);

    for(int i=0; i<count; i++)
    {
        testFoo++;
    }

    printf("Value: %d\n", testFoo.GetData());
}

++ testFoo veya testFoo ++ ile aynı sonuç kodunu almaya devam edersiniz. Aslında, kullanıcıdan gelen sayıyı okumadan, optimizer her şeyi bir sabite indirdi. Yani bu:

for(int i=0; i<10; i++)
{
    testFoo++;
}

printf("Value: %d\n", testFoo.GetData());

Sonuç olarak:

00401000  push        0Ah  
00401002  push        offset string "Value: %d\n" (402104h) 
00401007  call        dword ptr [__imp__printf (4020A0h)] 

Bu nedenle, postfix sürümünün daha yavaş olabileceği kesin olsa da, optimize ediciyi kullanmıyorsanız geçici kopyadan kurtulmak için yeterince iyi olabilir.


8
Burada her şeyin inline olduğu önemli noktayı not etmeyi unuttunuz. Operatörlerin tanımları mevcut değilse, hat dışı kodda yapılan kopyadan kaçınılamaz; inlining ile optim oldukça açıktır, bu yüzden herhangi bir derleyici bunu yapacaktır.
Blaisorblade

14

Google C ++ Stil Kılavuzu diyor ki:

Öncelik ve Tahmin

Yineleyiciler ve diğer şablon nesnelerle artış ve azalma işleçlerinin önek formunu (++ i) kullanın.

Tanım: Bir değişken arttığında (++ i veya i ++) veya azaltıldığında (--i veya i--) ve ifadenin değeri kullanılmadığında, önceden önizleme (azaltma) veya sonradan azaltma (azaltma) olup olmadığına karar verilmelidir.

Artıları: Dönüş değeri göz ardı edildiğinde, "pre" formu (++ i) hiçbir zaman "post" formundan (i ++) daha az verimli değildir ve genellikle daha verimlidir. Bunun nedeni artım sonrası (veya azalış) ifadesinin değeri olan i'nin bir kopyasının yapılmasını gerektirmesidir. İ bir yineleyici veya diğer skaler olmayan tipte ise, i kopyalamak pahalı olabilir. İki tür artış, değer göz ardı edildiğinde aynı şekilde davrandığından, neden her zaman artış öncesi olmaz?

Eksileri: C geleneği, ifade değeri kullanılmadığında, özellikle döngüler için artış sonrası kullanımı geliştirdi. "Konu" (i) tıpkı İngilizce'de olduğu gibi "fiil" (++) 'ten önce geldiğinden bazıları artım sonrası okumayı daha kolay bulur.

Karar: Basit skaler (nesne olmayan) değerler için bir formu tercih etmek için hiçbir neden yoktur ve biz de izin veririz. Yineleyiciler ve diğer şablon türleri için ön artışı kullanın.


1
"Karar: Basit skaler (nesne olmayan) değerler için bir formu tercih etmek için hiçbir neden yoktur ve ikisine de izin veririz. Yineleyiciler ve diğer şablon türleri için ön artışı kullanın."
Nosredna

2
Eh, ... ve bu nedir?
Sebastian Mach


4

Ben çok yakın zamanda Code Talk Andrew Koenig tarafından mükemmel bir yazı işaret etmek istiyorum.

http://dobbscodetalk.com/index.php?option=com_myblog&show=Efficiency-versus-intent.html&Itemid=29

Şirketimizde ayrıca, uygun olduğu durumlarda tutarlılık ve performans için ++ iter sözleşmesini kullanıyoruz. Ancak Andrew, performansa karşı niyetle ilgili ayrıntılı bir görünüm ortaya çıkarır. ++ iter yerine iter ++ kullanmak istediğimiz zamanlar vardır.

Bu nedenle, önce niyetinize karar verin ve eğer ön veya sonrası önemli değilse, ekstra nesnenin yaratılmasından kaçınarak ve fırlatarak performans avantajına sahip olacağından pre ile devam edin.



4

@Ketan

... performansa karşı niyetle ilgili ayrıntılı bilgi ortaya çıkarır. ++ iter yerine iter ++ kullanmak istediğimiz zamanlar vardır.

Açıkçası gönderi ve ön artış farklı semantiklere sahiptir ve eminim herkes sonuç kullanıldığında uygun operatörü kullanmanız gerektiğini kabul eder. Bence sonuç atıldığında ( fordöngülerde olduğu gibi ) ne yapılması gerekir . Cevabı bu soruya (IMHO) performans konuları en iyi ihmal edilebilir olduğundan, daha doğal ne yapmalıyım olmasıdır. Kendim ++iiçin daha doğal ama deneyimlerim bana bir azınlıkta olduğumu ve kullanmanın kodunuzu okuyan çoğu insan i++için daha az metal yüküne neden olacağını söylüyor .

Ne de olsa dilin " ++C" olarak adlandırılmamasının nedeni budur . [*]

[*] ++CDaha mantıklı bir isim olmayla ilgili zorunlu tartışmayı ekleyin .


4
@Motti: (şaka) Bjarne Stroustrup C ++ 'yı başlangıçta bir C programı üreten bir ön derleyici olarak kodladığını hatırlarsanız, C ++ adı mantıklıdır. Bu nedenle C ++ eski bir C değeri döndürdü. Ya da C ++ 'nın başından beri kavramsal olarak bir miktar kusurlu olduğunu geliştirmek olabilir.
kriss

4
  1. ++ i - dönüş değerini kullanmamak daha hızlı
  2. i ++ - dönüş değerini kullanarak daha hızlı

Ne zaman kullanmayan dönüş değeri derleyici durumunda geçici kullanmamayı garanti i ++ . Daha hızlı olduğu garanti edilmez, ancak daha yavaş olmadığı garanti edilir.

Zaman kullanarak dönüş değeri i ++ birbirine bağımlı olmayan yana işlemci boru hattına artım ve sol tarafı, her iki itme sağlar. ++ i boru hattını durdurabilir, çünkü işlemci ön artış işlemi sonuna kadar sol tarafa başlayamaz. Yine, bir boru hattı duraklaması garanti edilmez, çünkü işlemci yapışacak başka yararlı şeyler bulabilir.


3

Mark: Sadece operatör ++ 'ın inline edilecek iyi adaylar olduğunu belirtmek istedim ve eğer derleyici bunu seçerse, yedek kopya çoğu durumda elenecektir. (örneğin yineleyicilerin genellikle olduğu POD türleri.)

Bununla birlikte, çoğu durumda ++ iter kullanmak hala daha iyi bir stil. :-)


3

Operatörler arasında değer döndüren fonksiyonlar ve bunların nasıl uygulandığını düşündüğünüzde ++ive arasındaki performans farkı i++daha belirgin olacaktır. Ne olduğunu daha kolay anlamak için aşağıdaki kod örnekleri intsanki a struct.

++ideğişkeni artırır, ardından sonucu döndürür. Bu, yerinde ve minimum CPU zamanı ile yapılabilir, çoğu durumda sadece bir kod satırı gerektirir:

int& int::operator++() { 
     return *this += 1;
}

Ama aynı şey söylenemez i++.

Artış sonrası, i++genellikle artmadan önce orijinal değerin döndürüldüğü görülür . Ancak, bir işlev yalnızca bitince bir sonuç döndürebilir . Sonuç olarak, orijinal değeri içeren değişkenin bir kopyasını oluşturmak, değişkeni artırmak ve ardından orijinal değeri tutan kopyayı döndürmek gerekir:

int int::operator++(int& _Val) {
    int _Original = _Val;
    _Val += 1;
    return _Original;
}

Artım öncesi ve artım arasında işlevsel bir fark olmadığında, derleyici, ikisi arasında performans farkı olmayacak şekilde optimizasyon yapabilir. Ancak, structveya gibi bir bileşik veri türü classsöz konusu ise, kopya oluşturucu artım sonrası çağrılır ve derin bir kopya gerekiyorsa bu optimizasyonu gerçekleştirmek mümkün olmayacaktır. Bu nedenle, artım öncesi genellikle daha hızlıdır ve artım sonrası olandan daha az bellek gerektirir.


1

@ Mark: Önceki cevabımı sildim çünkü biraz çevikti ve sadece bunun için bir aşağı oy hak ediyordu. Aslında pek çok insanın kafasında ne olduğunu sorması açısından iyi bir soru olduğunu düşünüyorum.

Her zamanki cevap, ++ i i ++ 'dan daha hızlıdır ve şüphesiz, ama daha büyük soru "ne zaman umursamalısınız?"

Yineleyicileri artırmak için harcanan CPU zamanının oranı% 10'dan azsa, o zaman umursamazsınız.

Yineleyicilerin artırılmasında harcanan CPU zamanının oranı% 10'dan fazlaysa, bu ifadelerin hangi ifadeleri yaptığını görebilirsiniz. Yineleyicileri kullanmak yerine tamsayıları artırıp arttıramayacağınıza bakın. Şansınız olabilir ve bir anlamda daha az arzu edilebilir olsa da, şans oldukça iyidir, bu yineleyicilerde harcanan her zaman esasen tasarruf edersiniz.

Yineleyicinin artmasının zamanın% 90'ından fazlasını tükettiği bir örnek gördüm. Bu durumda, tamsayıyı arttırmak yürütme süresini esasen bu miktar kadar azalttı. (yani 10 kat daha hızlı)


1

@wilhelmtell

Derleyici geçici olanı kaldırabilir. Diğer konudan Verbatim:

C ++ derleyicisi, program davranışını değiştirse bile, yığın tabanlı geçici sürümleri ortadan kaldırmasına izin verilir. VC 8 için MSDN bağlantısı:

http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx


1
Alakalı değil. NRVO, "CC :: operator ++ (int)" içindeki t'yi arayana geri kopyalama gereksinimini ortadan kaldırır, ancak i ++ yine de arayanın yığınındaki eski değeri kopyalar. NRVO olmadan, i ++ bire t ve diğeri arayana geri 2 kopya oluşturur.
Blaisorblade

0

Performans avantajı olmayan yerleşik türlerde bile ++ i kullanmanızın bir nedeni, kendiniz için iyi bir alışkanlık oluşturmaktır.


3
Üzgünüm, ama bu beni rahatsız ediyor. Kim bunun hiç önemli olmadığında "iyi bir alışkanlık" olduğunu kim söyledi? İnsanlar onu disiplinlerinin bir parçası yapmak istiyorlarsa, sorun değil, ama önemli nedenleri kişisel zevk meselelerinden ayırt edelim.
Mike Dunlavey

@MikeDunlavey tamam, bu yüzden önemli olmadığında normalde hangi tarafı kullanıyorsunuz? xD ya biri ya da diğeri değil! post ++ (genel anlamı ile kullanıyorsanız. güncelleyin, güncelleyin, eski haline getirin) ++ pre (aşağı güncelleyin, iade) 'den tamamen daha düşüktür. daha sonra güncellemek istediğiniz durumda, programcı post ++ bile yapmaz. Zaten sahip olduğumuz zaman kopyalamayı boşa harcamak yok. kullandıktan sonra güncelleyin. sonra derleyicilerin sağduyuya sahip olmasını istersiniz.
birikintisi

@Puddle: Bunu duyduğumda: "asla daha az performansa sahip olmak istememen için hiçbir neden yok" Biliyorum "kuruş bilge - pound aptal". İlgili büyüklükleri takdir etmelisiniz. Sadece bu, zamanın% 1'inden fazlasını oluşturuyorsa, bir düşünce bile vermelisiniz. Bu konuda düşünüyorsan Genellikle, orada konum milyon kat daha büyük sorunlardır değil göz önünde bulundurarak, bu çok daha yavaş olması gerektiğinden daha yazılım kılan budur.
Mike Dunlavey

@MikeDunlavey egonuzu tatmin etmek için saçmalıktan yoksun kaldı. bilge bir keşiş gibi gelmeye çalışıyorsun, ama hiçbir şey söylemiyorsun. ilgili büyüklükler ... zamanın sadece% 1'inden fazlasına dikkat etmeniz gerekiyorsa ... xD mutlak salya. eğer verimsizse, bilmeye ve düzeltmeye değer. İşte tam da bu nedenle bunu düşünüyoruz! bu bilgiden ne kadar kazanabileceğimizden endişe etmiyoruz. ve daha az performans istemeyeceğinizi söylediğimde, devam edin, o zaman bir lanet senaryo açıklayın. BAY WISE!
Puddle

0

Her ikisi de aynı derecede hızlı;) İşlemci için aynı hesaplama olmasını istiyorsanız, sadece farklı olduğu sırada yapılır.

Örneğin, aşağıdaki kod:

#include <stdio.h>

int main()
{
    int a = 0;
    a++;
    int b = 0;
    ++b;
    return 0;
}

Aşağıdaki montajı yapın:

 0x0000000100000f24 <main+0>: push   %rbp
 0x0000000100000f25 <main+1>: mov    %rsp,%rbp
 0x0000000100000f28 <main+4>: movl   $0x0,-0x4(%rbp)
 0x0000000100000f2f <main+11>:    incl   -0x4(%rbp)
 0x0000000100000f32 <main+14>:    movl   $0x0,-0x8(%rbp)
 0x0000000100000f39 <main+21>:    incl   -0x8(%rbp)
 0x0000000100000f3c <main+24>:    mov    $0x0,%eax
 0x0000000100000f41 <main+29>:    leaveq 
 0x0000000100000f42 <main+30>:    retq

Bir ++ ve b ++ için bir anımsatıcı olduğunu görüyorsunuz, bu yüzden aynı işlem;)


OP, C ++ 'a sorduğu halde C'dir. C'de aynıdır. C ++ 'da daha hızlı ++ i; nesnesi nedeniyle. Ancak bazı derleyiciler artış sonrası operatörünü optimize edebilir.
Wiggler Jtag

0

Amaçlanan soru, sonucun ne zaman kullanılmadığıyla ilgilidir (bu, C sorusundan açıktır). Soru "topluluk wiki'si" olduğundan biri bunu düzeltebilir mi?

Erken optimizasyonlar hakkında Knuth sıklıkla alıntılanır. Doğru. ama Donald Knuth bu günlerde görebileceğiniz korkunç kodu asla savunmazdı. Hiç Java Tamsayılar arasında (= değil) a = b + c gördünüz mü? Bu, 3 boks / kutudan çıkarma dönüşümü anlamına gelir. Böyle şeylerden kaçınmak önemlidir. Ve gereksiz yere ++ i yerine i ++ yazmak da aynı hatadır. EDIT: phresnel bir yorum güzel koyar, bu "erken optimizasyon gibi erken optimizasyon kötüdür" olarak özetlenebilir.

İnsanların i ++ 'a daha alışkın olmaları bile, K&R tarafından yapılan kavramsal bir hatadan kaynaklanan talihsiz bir C mirasıdır (eğer niyet argümanını takip ederseniz, bu mantıklı bir sonuçtur; ve K&R'yi savunmak çünkü K&R anlamsızdır, harika, ancak dil tasarımcıları kadar iyi değiller; C tasarımında gets () 'dan strcpy ()' e strncpy () API 'ya kadar (1. günden beri strlcpy () API'sine sahip olmalıydı) sayısız hata var. ).

Btw, ben + + okumak için can sıkıcı bulmak için C ++ için yeterli kullanılmamış olanlardan biriyim. Yine de, doğru olduğunu kabul ettiğim için kullanıyorum.


Bir doktora üzerinde çalıştığını görüyorum. derleyici optimizasyonu ve bu tür şeylerle ilgileniyor. : Bu harika, ama akademiyi unutma bir yankı odası ve bu ilgilenen olabilir CS azından sağduyu genellikle kapının dışında bırakılmaz stackoverflow.com/questions/1303899/...
Mike Dunlavey

Asla ++idaha fazla sinir bozucu bulamadım i++(aslında, daha havalı buldum), ancak gönderinizin geri kalanı tam onayımı alıyor. Belki bir nokta ekleyin "erken optimizasyon gibi erken optimizasyon kötüdür"
Sebastian Mach

strncpyo sırada kullandıkları dosya sistemlerinde bir amaca hizmet etti; dosya adı 8 karakterlik bir arabellektir ve boş değerli sonlandırılması gerekmez. Dil evriminin geleceğinde 40 yıl görmediği için onları suçlayamazsınız.
MM

@MattMcNabb: 8 karakterlik bir MS-DOS'a özel değil mi? C, Unix ile icat edildi. Her neyse, strncpy bir noktaya sahip olsa bile, strlcpy eksikliği tam olarak haklı değildi: orijinal C bile taşmaması gereken, strlcpy gerektiren diziler içeriyordu; en fazla, sadece böcekleri istismar etmek isteyen saldırganları kaçırıyorlardı. Ancak bu sorunun tahmin edilmesinin önemsiz olduğunu söyleyemeyiz, bu yüzden yazımı yeniden yazarsam aynı tonu kullanmayacağım.
Blaisorblade

@Blaisorblade: Hatırladığım gibi, erken UNIX dosya adları 14 karakterle sınırlıydı. Eksikliği strlcpy()henüz icat edilmemişti gerçeğiyle haklı idi.
Keith Thompson

-1

Bilgelik taşlar ile millet sağlamak için zaman;) - C ++ postfix artışının önek artışı ile hemen hemen aynı davranmasını sağlamak için basit bir hile var (Bunu kendim için icat ettim, ama diğer insanların kodunda da gördüm, bu yüzden değilim tek başına).

Temel olarak, hile, dönüşten sonra artışı ertelemek için yardımcı sınıfı kullanmaktır ve RAII kurtarmaya gelir

#include <iostream>

class Data {
    private: class DataIncrementer {
        private: Data& _dref;

        public: DataIncrementer(Data& d) : _dref(d) {}

        public: ~DataIncrementer() {
            ++_dref;
        }
    };

    private: int _data;

    public: Data() : _data{0} {}

    public: Data(int d) : _data{d} {}

    public: Data(const Data& d) : _data{ d._data } {}

    public: Data& operator=(const Data& d) {
        _data = d._data;
        return *this;
    }

    public: ~Data() {}

    public: Data& operator++() { // prefix
        ++_data;
        return *this;
    }

    public: Data operator++(int) { // postfix
        DataIncrementer t(*this);
        return *this;
    }

    public: operator int() {
        return _data;
    }
};

int
main() {
    Data d(1);

    std::cout <<   d << '\n';
    std::cout << ++d << '\n';
    std::cout <<   d++ << '\n';
    std::cout << d << '\n';

    return 0;
}

Buluş, bazı ağır özel yineleyiciler kodu içindir ve çalışma süresini kısaltır. Önek vs postfix maliyeti şimdi bir referans ve eğer bu yoğun hareket eden özel operatör ise, önek ve postfix benim için aynı çalışma süresini verdi.


-5

++ii++değerinin eski bir kopyasını döndürmediğinden daha hızlıdır .

Ayrıca daha sezgisel:

x = i++;  // x contains the old value of i
y = ++i;  // y contains the new value of i 

Bu C örneği , beklediğiniz "12" yerine "02" yazdırır:

#include <stdio.h>

int main(){
    int a = 0;
    printf("%d", a++);
    printf("%d", ++a);
    return 0;
}

C ++ için aynı :

#include <iostream>
using namespace std;

int main(){
    int a = 0;
    cout << a++;
    cout << ++a;
    return 0;
}
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.