14

私は次のC++プログラムを書きました

class MyClass {
public:
        int i;
        int j;
        MyClass() {};
};

int main(void)
{
        MyClass inst;
        inst.i = 1;
        inst.j = 2;
}

そして私は編集しました。

# g++ program.cpp
# ls -l a.out
-rwxr-xr-x  1 root  wheel  4837 Aug  7 20:50 a.out

次に、#includeソースファイルにヘッダーファイルiostreamを追加し、再度コンパイルしました。

# g++ program.cpp
# ls -l a.out
-rwxr-xr-x  1 root  wheel  6505 Aug  7 20:54 a.out

予想通り、ファイルサイズが大きくなりました。

次のCプログラムも書きました

int main(void)
{
    int i = 1;
    int j = 2;
}

そして私はコンパイルしました

# gcc program.c
# ls -l a.out
-rwxr-xr-x  1 root  wheel  4570 Aug  7 21:01 a.out

次に、#includeヘッダーファイルstdio.hを作成し、再度コンパイルしました

# gcc program.c
# ls -l a.out
-rwxr-xr-x  1 root  wheel  4570 Aug  7 21:04 a.out

奇妙なことに、実行可能ファイルのサイズは同じままでした。

4

7 に答える 7

21

ソース ファイルにインクルードすることによりiostream、コンパイラは C++ 標準 I/O ライブラリをセットアップおよび破棄するコードを生成する必要があります。nmこれは、オブジェクト ファイルのシンボル (通常は関数) を示すからの出力を見るとわかります。

$ nm --demangle test_with_iostream
08049914 d _DYNAMIC
08049a00 d _GLOBAL_OFFSET_TABLE_
08048718 t global constructors keyed to main
0804883c R _IO_stdin_used
         w _Jv_RegisterClasses
080486d8 t __static_initialization_and_destruction_0(int, int)
08048748 W MyClass::MyClass()
         U std::string::size() const@@GLIBCXX_3.4
         U std::string::operator[](unsigned int) const@@GLIBCXX_3.4
         U std::ios_base::Init::Init()@@GLIBCXX_3.4
         U std::ios_base::Init::~Init()@@GLIBCXX_3.4
080485cc t std::__verify_grouping(char const*, unsigned int, std::string const&)
0804874e W unsigned int const& std::min<unsigned int>(unsigned int const&, unsigned int const&)
08049a3c b std::__ioinit
08049904 d __CTOR_END__
... (remaining output snipped) ...

(--demangleコンパイラによって「マングル」された C++ 関数名を取り、より意味のある名前を生成します。関数が実行可能ファイルに含まれている場合、最初の列はアドレスです。2 番目の列は型です。「t」は " text" セグメント。"U" は、他の場所 (この場合は C++ 共有ライブラリから) からリンクされたシンボルです。)

を含めずにソース ファイルから生成された関数と比較してくださいiostream

$ nm --demangle test_without_iostream
08049508 d _DYNAMIC
080495f4 d _GLOBAL_OFFSET_TABLE_
080484ec R _IO_stdin_used
         w _Jv_RegisterClasses
0804841c W MyClass::MyClass()
080494f8 d __CTOR_END__
... (remaining output snipped) ...

ソース ファイルに が含まれているiostream場合、コンパイラは、iostream.

ソース ファイルに のみが含まれている場合、C 標準 I/O ライブラリは、C 動的ライブラリで既に行われている以上の追加の初期化を必要としないためstdio.h、生成されたバイナリは を使用しないテストに似ています。これは、同じ出力iostreamを見るとわかります。nm

ただし、一般に、実行可能ファイルのサイズに基づいて、特定のソース ファイルによって生成されるコードの量に関する情報を直感的に把握しようとしても意味がありません。変更される可能性のあるものが多すぎます。コンパイラにデバッグ情報が含まれている場合、ソース ファイルの場所などの単純なことがバイナリを変更する可能性があります。

実行可能ファイルの内容をいじるのにもobjdump役立つ場合があります。

于 2009-08-07T18:42:38.227 に答える
9

通常、ヘッダー ファイルは単なる宣言であり、直接マシン コードが生成されることはありません。リンカは、CRT から未使用の関数を取り込まないほどスマートなので、その関数を使用せずに stdio.h をインクルードするだけでは、実行可能ファイルにコードが増えることはありません。

編集:コードを含むインライン関数、クラスなどを含めることができますが、実際に使用されるまで実行可能サイズが増加することはありません。

于 2009-08-07T18:22:39.780 に答える
7

iostreamにはコードが含まれています。stdio.hはしません。

より具体的には、iostreamの次の定義(リストされている以上のものがあり、コンパイラによって異なります)は、標準ライブラリで作成されたオブジェクトを参照し、コードにリンクされます。

extern istream &cin;
extern ostream &cout;
extern ostream &cerr;
extern ostream &clog;
于 2009-08-07T18:21:28.473 に答える
3

iostream にはいくつかの静的初期化がありますが、stdio.h には関数とその定義のみがあります。したがって、iostream を含めると、より大きなサイズの実行可能ファイルが生成されます。

于 2009-08-07T18:24:32.990 に答える
2

ヘッダーには、いくつかの<iostream>オブジェクト 'std::cin , 'std::coutstd::cerr、およびstd::clog. これらは、重要なコンストラクターとデストラクタを持つクラスのインスタンスです。これらはコードであり、リンクする必要があります。これにより、実行可能ファイルのサイズが増加します。

私の知る限り、<cstdio>コードは付属していないため、実行可能ファイルのサイズは増加しません。

于 2009-08-07T20:02:24.667 に答える
2

通常、ヘッダー ファイルにはコンパイラの情報のみが含まれ、実際のコードは含まれません。例えば:

struct foo {
  int x;
};

このような構造体の定義はヘッダーに表示されることがよくありますが、「foo」の処理方法に関する情報をコンパイラーに提供するだけなので、実際にコードを生成するわけではありません。foo が見つからない場合、コンパイルが終了すると情報が失われます。

実際、コードを生成するものがあると、通常はエラーが発生します例えば:

void foo() {
  printf("bar!\n");
}

これがヘッダーにあり、2 つの .c ファイルに含まれる場合、foo()関数は2 回生成されます。これにより、リンクでエラーが発生します。強い理由があればエラーを回避することは可能ですが、一般的に、ヘッダーで実際にコードを生成することは、可能であれば回避されます。

ここでの 1 つの例外は、C++ のインライン メンバーであることに注意してください。例えば:

class Foo {
  void bar() { /* ... */ }
};

技術的に言えば、このコードを含むすべてのファイルで bar() が生成されます。コンパイラは、エラーを回避するためにさまざまなトリックを実行します (弱いバインディングなど)。これにより、実際に実行可能ファイルのサイズが大きくなる可能性があり、おそらく<iostream>.

于 2009-08-07T18:23:54.957 に答える
0

iostream ファイルはいくつかのグローバル オブジェクトを宣言しました:

std::cout 、 std::cerr 、 std::cin は ostream 型です。

次に、コンパイラはそのクラスを取り込み、最終的なバイナリにコンパイルして、そのサイズを大幅に増やします。

于 2009-08-07T18:27:33.810 に答える