1. ホーム
  2. c++

[解決済み] 未使用のメンバ変数はメモリを食うか?

2023-02-10 20:38:09

質問

メンバ変数を初期化し、それ以上参照/使用しない場合、実行時に RAM を消費しますか?

struct Foo {
    int var1;
    int var2;

    Foo() { var1 = 5; std::cout << var1; }
};

上記の例では、メンバー 'var1' は値を取得し、コンソールに表示されます。しかし、'Var2' は全く使用されない。したがって、実行時にそれをメモリに書き込むことは資源の無駄遣いになります。コンパイラはこのような状況を考慮して、未使用の変数を無視するのでしょうか?

どのように解決するのですか?

C++の黄金律である "as-if"。 1 には次のように書かれています。 であれば 観測可能な動作 が未使用のデータメンバーの存在に依存しない場合、コンパイラはそれを最適化して取り除くことができます。 .

未使用のメンバ変数はメモリを消費しますか?

いいえ("本当に"未使用である場合)。


さて、ここで2つの疑問が浮かびます。

  1. 観測可能な動作がメンバーの存在に依存しないのはどんな場合か?
  2. 実際のプログラムではそのような状況は発生するのでしょうか?

まずは例から。

#include <iostream>

struct Foo1
{ int var1 = 5;           Foo1() { std::cout << var1; } };

struct Foo2
{ int var1 = 5; int var2; Foo2() { std::cout << var1; } };

void f1() { (void) Foo1{}; }
void f2() { (void) Foo2{}; }

もし gcc にこの翻訳ユニットをコンパイルするよう とすると、出力されます。

f1():
        mov     esi, 5
        mov     edi, OFFSET FLAT:_ZSt4cout
        jmp     std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
f2():
        jmp     f1()

f2 と同じです。 f1 と同じで、実際の Foo2::var2 . ( Clang は似たようなことをします ).

ディスカッション

これは2つの理由で違うという人もいるかもしれません。

  1. これはあまりにも些細な例です。
  2. 構造体は完全に最適化されているため、カウントされません。

さて、良いプログラムとは、複雑なものを単純に並べるのではなく、単純なものを賢く複雑に組み立てるものです。実際の生活では、コンパイラが最適化するよりも、単純な構造を使って大量の単純な関数を書きます。たとえば

bool insert(std::set<int>& set, int value)
{
    return set.insert(value).second;
}

これは正真正銘、データ・メンバー(ここでは。 std::pair<std::set<int>::iterator, bool>::first )が使われていない本物の例です。何だと思いますか? それは最適化されて離れている ( ダミーセットによる単純な例 を使うこともできます)。

今こそ Max Langhofの素晴らしい回答を読む を読むのに最適なタイミングでしょう (私のためにアップヴォートしてください)。最終的に、なぜ構造の概念が、コンパイラーが出力するアセンブリ レベルで意味をなさないのかを説明しています。

"しかし、X を行うと、未使用のメンバーが最適化されてしまうというのは問題です!"

この回答は間違っているに違いないと主張するコメントが多数寄せられています。 assert(sizeof(Foo2) == 2*sizeof(int)) のような)何らかの操作が何かを壊してしまうからです。

Xがプログラムの観察可能な動作の一部である場合 2 である場合、コンパイラは最適化することができません。プログラム上で観察可能な効果を持つであろう "unused"データメンバーを含むオブジェクトに対する多くの操作があります。もしそのような操作が行われた場合、またはコンパイラが何も行われていないことを証明できない場合、その "unused" データメンバーはプログラムの観察可能な動作の一部となります。 であり、最適化されることはありません。 .

観察可能な動作に影響を与える操作には、これらに限定されませんが、以下のものがあります。

  • オブジェクトのタイプのサイズを取る ( sizeof(Foo) ),
  • 未使用のデータメンバの後に宣言されたデータメンバのアドレスを取得します。
  • のような関数でオブジェクトをコピーする。 memcpy ,
  • オブジェクトの表現を操作する(例えば memcmp ),
  • としてオブジェクトを修飾する 揮発性 ,
  • その他 .

1)

[intro.abstract]/1

この文書における意味記述は,パラメタ化された非決定論的抽象機械を定義する。この文書は,適合する実装の構造に対して,何の要件も課さない。特に、抽象機械の構造をコピーしたり、エミュレートしたりする必要はない。むしろ、適合する実装は、以下に説明するように、抽象機械の観察可能な動作をエミュレートすること(のみ)が要求される。

2) アサートのパスやフェイルがそうであるように