C ++では、i ++と++ iの間にパフォーマンスの違いはありますか?


352

私たちは質問をしている間の性能差があるi++++i Cでは

C ++の答えは何ですか?


この2つのタグがこの種の質問を見つける最も簡単な方法であるため、タグを付け直しました。私は、まとまりのないタグがない他の人たちも調べて、まとまりのあるタグを与えました。
ジョージストッカー

104
C ++と++ Cの使用にパフォーマンスの違いはありますか?
new123456 2011年

2
記事:イテレータで後置演算子it ++の代わりに前置インクリメント演算子++ itを使用することは妥当ですか?- viva64.com/en/b/0093

回答:


426

[エグゼクティブサマリー:使用++iする特別な理由がない場合に使用しますi++。]

C ++の場合、答えはもう少し複雑です。

場合はi、単純なタイプ(C ++クラスのインスタンスではない)で、その後、Cのために与えられた答えが(「いいえパフォーマンスの違いはありません」)コンパイラがコードを生成しているため、保持しています。

ただし、iがC ++クラスのインスタンスである場合、i++および++ioperator++関数の1つを呼び出しています。これらの関数の標準ペアは次のとおりです。

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

コンパイラはコードを生成せず、operator++関数を呼び出すだけなので、tmp変数とそれに関連するコピーコンストラクターを最適化する方法はありません。コピーコンストラクタが高価な場合、これはパフォーマンスに大きな影響を与える可能性があります。


3
コンパイラが回避できるのは、別のコメントで述べられているように、NRVOを介して呼び出し元でtmpを割り当てることにより、tmpを返す2番目のコピーです。
Blaisorblade、2009年

7
operator ++がインライン化されている場合、コンパイラーはこれを完全に回避できませんか?
エデュアルド-ガブリエルムンテアヌ

16
はい。operator++がインライン化され、tmpが使用されない場合は、tmpオブジェクトのコンストラクタまたはデストラクタに副作用がない限り削除できます。
Zan Lynx

5
@kriss:CとC ++の違いは、Cでは演算子がインライン化されることが保証され、その時点で適切なオプティマイザが違いを取り除くことができるということです。代わりに、C ++ではインライン展開を想定できません-常にではありません。
Blaisorblade、2012年

3
答えが、動的に割り当てられた(ヒープ)メモリへのポインタ(自動、スマート、プリミティブのいずれか)を保持するクラスについて何か言及した場合、コピーコンストラクタは必然的にディープコピーを実行する場合、+ 1します。そのような場合、議論はありません。++ iはおそらくi ++よりも桁違いに効率的です。彼らの鍵は、ポストインクリメントのセマンティクスが実際にアルゴリズムで必要とされない場合は常にプリインクリメントを使用する習慣を身に付けることです。そうすれば、方法に関係なく、本質的に効率を高めるコードを書く習慣になります。コンパイラは最適化できます。
Phonetagger、

64

はい。有る。

++演算子は、関数として定義されている場合とされていない場合があります。プリミティブ型(int、doubleなど)の場合、演算子が組み込まれているため、コンパイラーはおそらくコードを最適化できます。しかし、++演算子を定義するオブジェクトの場合、状況は異なります。

operator ++(int)関数はコピーを作成する必要があります。これは、Postfix ++が保持する値とは異なる値を返すことが期待されているためです。一時変数に値を保持し、その値をインクリメントして一時変数を返す必要があります。operator ++()、prefix ++の場合、コピーを作成する必要はありません。オブジェクトは自身をインクリメントしてから、それ自体を返すことができます。

ポイントの説明は次のとおりです。

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)を呼び出すたびにコピーを作成する必要があり、コンパイラーはそれについて何もできません。選択肢が与えられたら、operator ++();を使用します。この方法では、コピーを保存しません。これは、多くの増分(大きなループ?)や大きなオブジェクトの場合に重要になることがあります。


2
「プリインクリメント演算子は、コードにデータ依存関係を導入します。CPUは、値が式で使用される前に、インクリメント操作が完了するのを待つ必要があります。深くパイプライン化されたCPUでは、これによりストールが発生します。データ依存性はありませんポストインクリメント演算子。」(ゲームエンジンアーキテクチャ(第2版))したがって、ポストインクリメントのコピーが計算集約的でない場合でも、プリインクリメントを上回る可能性があります。
Matthias

Postfixコードでは、これはどのように機能するかC t(*this); ++(*this); return t;2行目では、thisポインターを右にインクリメントしているので、これをインクリメントしている場合はどのようtに更新されますか。これの値はすでにコピーされていtませんか?
rasen58 2017

The operator++(int) function must create a copy.いいえそうではありません。これ以上のコピーはありませんoperator++()
Severin Pappadeux 2019

47

以下は、インクリメント演算子が異なる翻訳単位にある場合のベンチマークです。g ++ 4.5のコンパイラ。

今のところスタイルの問題は無視してください

// 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)の増分

テスト

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

結果

仮想マシン上のg ++​​ 4.5での結果(タイミングは秒単位):

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)増分

テスト

次のファイルを見てみましょう。

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

インクリメントでは何もしません。これは、増分が一定の複雑さを持つ場合をシミュレートします。

結果

結果は大きく異なります:

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

結論

パフォーマンスに関して

以前の値が必要ない場合は、事前増分を使用することを習慣にしてください。組み込み型でも一貫性があれば、それに慣れ、組み込み型をカスタム型に置き換えても、不必要なパフォーマンス低下のリスクを負うことはありません。

意味論的に

  • i++と言うincrement i, I am interested in the previous value, though
  • ++iincrement i, I am interested in the current valueまたはと言いincrement i, no interest in the previous valueます。繰り返しますが、今は慣れていなくても慣れます。

クヌース。

時期尚早の最適化は、すべての悪の根源です。時期尚早の悲観論もそうです。


1
興味深いテスト。現在、ほぼ2年半後、gcc 4.9とClang 3.4は同様の傾向を示しています。Clangは両方とも少し高速ですが、prefixとpostfixの差異はgccよりも悪いです。
靴下を噛む

私が本当に見たいのは、++ i / i ++が違いをもたらす実際の例です。たとえば、どのstdイテレータでも違いがありますか?
Jakob Schou Jensen

@JakobSchouJensen:これらは実世界の例となるように意図されていました。複雑なツリー構造(kdツリー、クワッドツリーなど)または式テンプレートで使用される大規模なコンテナー(SIMDハードウェアでのデータスループットを最大化するため)を備えた大規模アプリケーションを検討してください。もしそれがそこで違いを生むなら、それが意味論的に必要ではないのに、なぜ特定のケースのためにポストインクリメントにフォールバックするのか、私には本当にわからない。
Sebastian Mach

@phresnel:operator ++が日常の式テンプレートにあるとは思いません-これの実際の例はありますか?operator ++の一般的な使用法は、整数と反復子です。それが違いがあるかどうかを知るのは興味深いことだと思いました(もちろん整数には違いはありませんが、反復子です)。
Jakob Schou Jensen 2015

@JakobSchouJensen:実際のビジネス例はありませんが、数を数える計算アプリケーションがいくつかあります。Wrtイテレーター、慣用的なC ++スタイルで記述されたレイトレーサーを検討してください。for (it=nearest(ray.origin); it!=end(); ++it) { if (auto i = intersect(ray, *it)) return i; }実際のツリー構造(BSP、kd、Quadtree、Octree Gridなど)を気にしないように、深さ優先トラバーサルのイテレーターがあります。このようなイテレータは、例えば、いくつかの状態を維持する必要があるだろうparent nodechild nodeindex及びそのようなもの。全体として、私のスタンスは、たとえいくつかの例しか存在しなくても、...
セバスチャンマッハ

20

postfixの場合、コンパイラーが一時的な変数のコピーを最適化できないと言うのは完全に正しくはありません。VCを使用した簡単なテストでは、少なくとも特定の場合にそれが可能であることを示しています。

次の例では、生成されるコードは、たとえば接頭辞と後置で同じです。

#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でもtestFoo ++でも、同じ結果のコードが得られます。実際、ユーザーからのカウントを読み込まずに、オプティマイザはすべてを定数にしました。したがって、この:

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

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

結果は次のとおりです。

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

したがって、postfixのバージョンが遅くなる可能性があることは確かですが、オプティマイザが一時的なコピーを使用しない場合はそれを取り除くのに十分な場合もあります。


8
ここではすべてがインライン化されているという重要な点に注意するのを忘れていました。演算子の定義が利用できない場合、行外のコードで行われたコピーは避けられません。optimのインライン化は非常に明白なので、どのコンパイラーでも実行できます。
Blaisorblade、2009年

14

GoogleのC ++スタイルガイドは言います:

プリインクリメントとプリデクリメント

イテレータや他のテンプレートオブジェクトでは、増分演算子と減分演算子のプレフィックス形式(++ i)を使用します。

定義:変数がインクリメント(++ iまたはi ++)またはデクリメント(--iまたはi--)され、式の値が使用されない場合、プリインクリメント(デクリメント)またはポストインクリメント(デクリメント)のいずれかを決定する必要があります。

長所:戻り値が無視される場合、「前」形式(++ i)は「後」形式(i ++)よりも効率が悪くなることはなく、多くの場合より効率的です。これは、ポストインクリメント(またはデクリメント)では、式の値であるiのコピーを作成する必要があるためです。iがイテレータまたはその他の非スカラー型の場合、iをコピーすると負荷がかかる可能性があります。値が無視された場合、2つのタイプの増分は同じように動作するので、なぜ常に事前増分ではないのですか?

短所: Cでは、式の値が使用されない場合、特にforループでポストインクリメントを使用するという伝統が発達しました。英語のように、「件名」(i)が「動詞」(++)の前にあるため、ポストインクリメントが読みやすくなる人もいます。

決定:単純なスカラー(非オブジェクト)値の場合、1つの形式を選択する理由はなく、どちらも許可します。イテレータおよびその他のテンプレートタイプの場合は、事前増分を使用します。


1
「決定:単純なスカラー(非オブジェクト)値の場合、1つのフォームを選択する理由はなく、どちらかを使用できます。イテレーターおよびその他のテンプレートタイプの場合は、事前増分を使用します。」
Nosredna 2009年

2
えっと...、そしてそれは何ですか?
セバスチャンマッハ

回答で言及されているリンクは現在壊れています
カロル

4

最近、Andrew KoenigによるCode Talkに関する優れた投稿を指摘したいと思います。

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

当社では、必要に応じて、一貫性とパフォーマンスのために++ iterの規則を使用しています。しかし、Andrewは意図とパフォーマンスに関して見過ごされている詳細を挙げています。++ iterの代わりにiter ++を使用したい場合があります。

そのため、最初に目的を決定し、事前または事後が問題ではない場合は、事前オブジェクトを作成します。これにより、余分なオブジェクトの作成を避け、それをスローすることでパフォーマンスが向上します。



4

@ケタン

...意図とパフォーマンスに関する見過ごされている詳細を引き上げます。++ iterの代わりにiter ++を使用したい場合があります。

明らかにpostとpre-incrementのセマンティクスは異なり、結果が使用される場合は適切な演算子を使用する必要があることに誰もが同意するに違いありません。問題は、(forループのように)結果が破棄されたときに何をすべきかだと思います。この質問(IMHO)への回答は、パフォーマンスの考慮はせいぜい無視できるため、より自然なことを行う必要があるということです。私自身の++iほうが自然ですが、私の経験から、私は少数派であり、使用i++することで、コードを読むほとんどの人にとってメタルオーバーヘッドが少なくなることがわかります。

結局それが言語が「++C」と呼ばれない理由です。[*]

[*] ++Cより論理的な名前であることについての強制的な議論を挿入します。


4
@Motti:(冗談)Bjarne Stroustrup C ++を最初にCプログラムを生成するプリコンパイラとしてコーディングしたことを思い出せば、C ++名は論理的です。したがって、C ++は古いC値を返しました。あるいは、C ++には最初から概念的に多少欠陥があることを強調するかもしれません。
kris

4
  1. ++ i-戻り値を使用ない方が速い
  2. i ++ - 戻り値を使用してより高速

ときに使用していない、戻り値をコンパイラがある場合には一時的に使用しないことが保証されてI ++。高速であることが保証されていませんが、低速ではないことが保証されています。

戻り値を使用する場合、i ++は、プロセッサがインクリメントと左側の両方を互いに依存しないため、パイプラインにプッシュすることを許可します。++ iは、プリインクリメント操作が最後まで蛇行するまでプロセッサが左側を開始できないため、パイプラインを停止させる可能性があります。繰り返しになりますが、プロセッサは他の便利なものを見つける可能性があるため、パイプラインのストールは保証されません。


3

マーク:operator ++はインライン化するのに適した候補であることを指摘したかっただけです。コンパイラーがインライン化することを選択した場合、ほとんどの場合、冗長なコピーは削除されます。(例えば、通常イテレータであるPODタイプ)

とは言っても、ほとんどの場合、++ iterを使用する方がスタイルは優れています。:-)


3

とのパフォーマンスの違いは++ii++演算子を値を返す関数と見なし、それらを実装する方法を考えると、より明確になります。何が起こっているのかを理解しやすくするために、次のコード例ではint、あたかものように使用しstructます。

++i、変数をインクリメントし、その後、結果を返します。これはインプレースで実行でき、CPU時間は最小限で済みます。多くの場合、必要なコードは1行だけです。

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

しかし、同じことは言えませんi++

ポストインクリメントはi++、インクリメントする前に元の値を返すと見なされることがよくあります。ただし、関数は終了時にのみ結果を返すことができます。その結果、元の値を含む変数のコピーを作成し、変数をインクリメントして、元の値を保持するコピーを返すことが必要になります。

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

プリインクリメントとポストインクリメントの間に機能的な違いがない場合、コンパイラーは最適化を実行して、2つの間にパフォーマンスの違いがないようにすることができます。ただし、structまたはなどの複合データ型が含まれている場合class、コピーコンストラクターはポストインクリメントで呼び出され、ディープコピーが必要な場合にこの最適化を実行することはできません。そのため、前置インクリメントは通常、後置インクリメントよりも高速で、必要なメモリも少なくなります。


1

@Mark:少しフリップだったので、以前の回答を削除しました。多くの人の頭にあることを問うという意味では、いい質問だと思います。

通常の答えは、++ iはi ++よりも速いということです、そして間違いなくそれは確かですが、より大きな問題は、「いつあなたは気にすべきですか?」

イテレータの増分に費やされたCPU時間の割合が10%未満の場合、気にする必要はありません。

イテレーターの増分に費やされたCPU時間の割合が10%を超える場合、どのステートメントがその反復を行っているかを確認できます。イテレータを使用するのではなく、整数をインクリメントできるかどうかを確認してください。可能性はあります。ある意味では望ましくないかもしれませんが、可能性はかなり高く、基本的にこれらのイテレータで費やされる時間をすべて節約できます。

イテレータの増分が90%以上の時間を消費している例を見ました。その場合、整数でインクリメントすると、実行時間は本質的にその分だけ短縮されます。(すなわち、10倍のスピードアップより良い)


1

あずきっく

コンパイラーは一時ファイルを省略できます。他のスレッドから逐語的:

C ++コンパイラは、プログラムの動作を変更する場合でも、スタックベースの一時を削除することができます。VC 8のMSDNリンク:

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


1
それは関係ありません。NRVOは "CC :: operator ++(int)"のtを呼び出し元にコピーする必要を回避しますが、i ++は呼び出し元のスタックに古い値をコピーします。NRVOがない場合、i ++は2つのコピーを作成します。1つはtに、もう1つは呼び出し元に戻ります。
Blaisorblade、2009年

0

パフォーマンス上の利点がない組み込み型でも++ iを使用する必要がある理由は、自分自身に良い習慣を作るためです。


3
すみませんが、それは私を困らせます。それがほとんど問題にならないとき、それは「良い習慣」であると誰が言いますか?人々がそれを自分の分野の一部にしたいのであればそれは問題ありませんが、重要な理由と個人的な好みの問題を区別しましょう。
Mike Dunlavey、2009

@MikeDunlaveyわかりました。問題がないときに、通常どちらの側を使用しますか?xDそれはどちらか一方ではありません!post ++(一般的な意味でそれを使用している場合、それを更新し、古いものを返す)は、++ pre(それを更新し、返す)よりも完全に劣っています。パフォーマンスを低下させたい理由は決してありません。後で更新したい場合は、プログラマーはpost ++すらしません。すでに手元にあるので、時間を無駄にコピーする必要はありません。使用後は更新してください。それからあなたがそれが欲しかった常識を持つコンパイラー。
水たまり

@水たまり:これを聞いたとき:「パフォーマンスを下げたい理由は決してない」私は「ペニーワイズ-ポンドバカ」を聞いていることを知っています。含まれている大きさを正しく理解する必要があります。これが関与する時間の1%以上を占める場合にのみ、考えさえする必要があります。通常、これについて考えている場合、考慮していない数百万倍の大きな問題があり、これがソフトウェアの速度をはるかに遅くする原因です。
Mike Dunlavey、2018

@MikeDunlaveyはあなたのエゴを満たすためにナンセンスを吐き返しました。賢い僧侶のように聞こえようとしているのに、何も言っていない。必要なマグニチュード... 1%を超える時間しか気にかけない場合... xD絶対ドリブル。効率が悪い場合は、知って修正する価値があります。私たちはここで、まさにその理由のためにこれを熟考しています!この知識からどれだけの利益が得られるかは気にしていません。そして、パフォーマンスを下げたくないと言ったときは、先に進んで、いまいましいシナリオを1つ説明してください。MR WISE!
水たまり2018

0

どちらも同じくらい高速です;)プロセッサーで同じ計算をしたい場合は、実行される順序が異なるだけです。

たとえば、次のコード:

#include <stdio.h>

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

次のアセンブリを作成します。

 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

a ++とb ++の場合はニーモニックが含まれているので、同じ操作です;)


それはCですが、OPはC ++に尋ねました。Cでも同じです。C ++では++ iの方が高速です。その目的のため。ただし、一部のコンパイラーはポストインクリメント演算子を最適化する場合があります。
Wiggler Jtag 2015

0

意図した質問は、結果が未使用の場合に関するものでした(Cの質問から明らかです)。質問は「コミュニティーwiki」なので、誰かがこれを修正できますか?

時期尚早の最適化については、Knuthがしばしば引用されます。そのとおり。しかし、ドナルド・クヌースはあなたが最近目にすることができるその恐ろしいコードを決して守らないでしょう。Java Integer(intではない)の間でa = b + cを見たことがありますか?これは、3つのボックス化/ボックス化解除の変換に相当します。そのようなものを避けることは重要です。そして、++ iの代わりにi ++を無駄に書くのも同じ間違いです。編集:フレネルはそれをコメントにうまく入れているので、これは「時期尚早な悲観化と同様に時期尚早な最適化は悪い」と要約することができます。

人々がi ++に慣れているという事実でさえ、K&Rによる概念上の誤りによって引き起こされた、残念なCの遺産です(意図的な議論に従えば、それは論理的な結論です。K&RであるK&Rを守ることは無意味なので、すばらしいですが、言語デザイナーとしてはすばらしいものではありません。gets()からstrcpy()からstrncpy()APIまで、Cデザインには無数の間違いがあります(1日目からstrlcpy()APIがあったはずです)。 )。

ところで、私はC ++が++ iを読むのが面倒だと気づくのに十分に慣れていない人の一人です。それでも、私はそれが正しいことを認めているので、それを使用します。


博士号を取得しているようですね。コンパイラの最適化とそのようなことに関心があります。それは素晴らしいことだが、学界を忘れないでください反響室である、と常識が、多くの場合、ドアの外に残されます、少なくともCSにあなたは、このに興味があるかもしれない:stackoverflow.com/questions/1303899/...
マイクDunlavey

私は(実際にはもっと涼しいと感じました)++iよりもいらいらすることはありませんでしたi++が、あなたの投稿の残りの部分は私の完全な承認を得ています。「時期尚早の悲観化と同様に時期尚早の最適化は悪である」というポイントを追加するかもしれません
Sebastian Mach

strncpy彼らが当時使用していたファイルシステムで目的を果たした。ファイル名は8文字のバッファであり、ヌル文字で終了する必要はありませんでした。言語進化の未来への40年を見ていないためにそれらを非難することはできません。
MM

@MattMcNabb:8文字のファイル名はMS-DOS専用ではありませんか?CはUnixで発明されました。とにかく、たとえstrncpyにポイントがあったとしても、strlcpyの欠如は完全には正当化されませんでした。元のCでさえ、オーバーフローしてはならない配列があり、strlcpyが必要でした。せいぜい、彼らはバグを悪用しようとする攻撃者を逃しているだけでした。しかし、この問題を予測するのは簡単だったとは言えないので、投稿を書き直した場合、同じ口調を使うことはありません。
Blaisorblade 14

@Blaisorblade:覚えているように、初期のUNIXファイル名は14文字に制限されていました。の欠如は、strlcpy()まだ発明されていないという事実によって正当化されました。
キース・トンプソン、

-1

人々に知恵の宝石を提供する時間です;)-C ++のポストフィックスのインクリメントをプレフィックスのインクリメントとほぼ同じように動作させる簡単なトリックがあります(これを自分で発明しましたが、他の人のコードでも見たので、私はそうではありません一人)。

基本的に、トリックはヘルパークラスを使用して戻り後のインクリメントを延期することであり、RAIIが救助に来る

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

発明されたのは、いくつかの重いカスタムイテレータコード用で、実行時間を短縮します。接頭辞と接尾辞のコストは1つの参照になりました。これが大量の移動を行うカスタムオペレーターの場合、接頭辞と接尾辞は同じ実行時間をもたらしました。


-5

++ii++値の古いコピーを返さないためよりも高速です。

また、より直感的です。

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

このCの例は、予想される "12"ではなく "02"を出力します。

#include <stdio.h>

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

C ++についても同様です

#include <iostream>
using namespace std;

int main(){
    int a = 0;
    cout << a++;
    cout << ++a;
    return 0;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.