为什么不';tc头文件增加了二进制';s码? 我写了以下C++程序 class MyClass { public: int i; int j; MyClass() {}; }; int main(void) { MyClass inst; inst.i = 1; inst.j = 2; }

为什么不';tc头文件增加了二进制';s码? 我写了以下C++程序 class MyClass { public: int i; int j; MyClass() {}; }; int main(void) { MyClass inst; inst.i = 1; inst.j = 2; },c++,executable,header-files,C++,Executable,Header Files,我编译了 # g++ program.cpp # ls -l a.out -rwxr-xr-x 1 root wheel 4837 Aug 7 20:50 a.out 然后,我将头文件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) {

我编译了

# g++ program.cpp
# ls -l a.out
-rwxr-xr-x  1 root  wheel  4837 Aug  7 20:50 a.out
然后,我将头文件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
然后,我将头文件stdio.h包含进去,然后再次编译

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

奇怪的是,可执行文件的大小保持不变。

iostream包含代码。stdio.h没有

更具体地说,iostream中的以下定义(列出的不止一个,并且因编译器而异)引用了在标准库中创建的对象,这些对象随后链接到您的代码中:

extern istream &cin;
extern ostream &cout;
extern ostream &cerr;
extern ostream &clog;

头文件通常只是声明,不会直接导致生成机器代码。链接器足够聪明,不会从CRT中提取未使用的函数,因此只包含stdio.h而不使用其任何函数不会在可执行文件中产生更多代码


编辑:它们可以包括内联函数、类等,这些函数、类等确实包含代码,但在实际使用之前,它们不应导致可执行文件大小的增加。

通常来说,头文件只包含编译器的信息,而不包含实际代码。例如:

struct foo {
  int x;
};
void foo() {
  printf("bar!\n");
}
class Foo {
  void bar() { /* ... */ }
};
像这样的结构定义经常出现在标题中,但它们实际上不会导致生成代码,因为它们只向编译器提供有关如何处理“foo”的信息,如果它以后看到它的话。如果它没有看到foo,则编译完成后信息将丢失

事实上,任何生成代码的东西通常都会产生错误。例如:

struct foo {
  int x;
};
void foo() {
  printf("bar!\n");
}
class Foo {
  void bar() { /* ... */ }
};
如果这是在头文件中,并且包含在两个.c文件中,则将生成两次
foo()
函数。这将导致链接出错。如果您有充分的理由这样做,那么可以避免错误,但是如果可能的话,通常可以避免在头中实际生成代码

注意,这里有一个例外是C++中的内联成员。例如:

struct foo {
  int x;
};
void foo() {
  printf("bar!\n");
}
class Foo {
  void bar() { /* ... */ }
};

从技术上讲,包含此代码的每个文件中都会生成bar()。编译器使用各种技巧来避免错误(弱绑定等)。这可能确实会增加可执行文件的大小,这可能就是您在iostream中看到的情况,而在stdio.h中只有函数和它们的定义。因此,包含iostream将生成更大的可执行文件。

iostream文件声明了一些全局对象:

std::cout、std::cerr、std::cin都是ostream类型


<编译器>将编译该类并将其编译为最终的二进制文件,并将其添加到其大小。

在源文件中包含<代码> IoSturis/Cuff>,编译器需要生成代码来设置和拆除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) ...
当源文件包含
iostream
时,编译器生成了几个没有
iostream
的函数

当源文件仅包含
stdio.h
时,生成的二进制文件与不包含
iostream
的测试类似,因为C标准I/O库不需要任何超出C动态库中已发生的额外初始化。您可以通过查看相同的
nm
输出来看到这一点

不过,一般来说,试图根据可执行文件的大小直观地了解特定源文件生成的代码量是没有意义的;可能会有太多的更改,如果编译器包含调试信息,那么源文件的位置等简单的事情可能会更改二进制文件

您还可能会发现,
objdump
对于查找可执行文件的内容非常有用。

头附带了几个对象,'std::cin
,'std::cout
std::cerr
,以及
std::clog
。这些是具有非平凡构造函数和析构函数的类的实例。这些是代码,必须链接。这就是增加可执行文件大小的原因


好吧,
没有代码,所以这就是为什么没有增加可执行文件的大小

extern
定义不生成代码;它们只是让编译器知道它们存在,因此编译器可以在实际使用时引用它们。是的,这是一个有价值的澄清。我不认为它们是外部的,即使它们是外部的,也必须有一些定义。它们的CTOR/DTOR是可执行文件大小增加的一个(可能是唯一的)原因。如果它们不是
extern
(至少在VC++中是这样),它们将在包含
iostream
的每个源文件中实例化。。。不是你想要的。是的,但这并不能解释为什么
会增加可执行文件的大小。