2009-08-07 10 views
10

Tôi đã viết chương trình C++ sau đâyTại sao các tệp tiêu đề C không làm tăng kích thước của tệp nhị phân?

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

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

và tôi biên soạn.

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

Sau đó, tôi #include d tệp tiêu đề iostream trong tệp nguồn và tôi biên soạn lại.

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

Kích thước tệp, như mong đợi, đã tăng lên.

Tôi cũng đã viết chương trình C sau

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

và tôi biên soạn

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

Sau đó, tôi #include d file header stdio.h và tôi biên soạn lại

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

Kỳ lạ thay, kích thước của các tệp thực thi vẫn giữ nguyên.

Trả lời

18

Bằng cách bao gồm iostream trong tệp nguồn của bạn, trình biên dịch cần tạo mã để thiết lập và xé bỏ thư viện I/O chuẩn C++. Bạn có thể thấy điều này bằng cách nhìn vào sản lượng từ nm, trong đó cho thấy những biểu tượng (thường chức năng) trong hồ sơ đối tượng của bạn:

$ 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() [email protected]@GLIBCXX_3.4 
     U std::string::operator[](unsigned int) [email protected]@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 mất C tên hàm ++ "nham nhở" bởi bởi trình biên dịch và tạo ra ý nghĩa hơn Tên cột đầu tiên là địa chỉ, nếu hàm được bao gồm trong tệp thực thi, cột thứ hai là kiểu. "t" là mã trong phân đoạn "văn bản". "U" là các ký hiệu được liên kết từ các vị trí khác; trường hợp, từ thư viện được chia sẻ C++.)

So sánh điều này với các chức năng được tạo từ tệp nguồn của bạn mà không bao gồm 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) ... 

Khi tệp nguồn của bạn bao gồm iostream, trình biên dịch tạo ra một số hàm không có mặt mà không có iostream.

Khi tệp nguồn của bạn chỉ bao gồm stdio.h, nhị phân được tạo tương tự như kiểm tra không có iostream, vì thư viện I/O tiêu chuẩn C không cần bất kỳ khởi tạo bổ sung nào vượt quá những gì đã xảy ra trong thư viện động C. Bạn có thể thấy điều này bằng cách nhìn vào đầu ra nm, giống hệt nhau.

Nói chung, mặc dù, cố gắng intuit thông tin về số lượng mã được tạo ra bởi một tệp nguồn cụ thể dựa trên kích thước của tệp thực thi sẽ không có ý nghĩa; có quá nhiều thứ có thể thay đổi và những thứ đơn giản như vị trí của tệp nguồn có thể thay đổi nhị phân nếu trình biên dịch bao gồm thông tin gỡ lỗi.

Bạn cũng có thể tìm thấy objdump hữu ích để xem xung quanh tại nội dung của tệp thi hành.

7

iostream bao gồm mã. stdio.h thì không.

Cụ thể hơn, các định nghĩa sau đây trong iostream (có hơn niêm yết, và thay đổi theo trình biên dịch) đối tượng tham chiếu được tạo ra trong thư viện chuẩn, mà sau đó được liên kết vào mã của bạn:

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

'định nghĩa bên ngoài không tạo mã; chúng chỉ cho trình biên dịch biết chúng tồn tại, do đó trình biên dịch có thể tham khảo chúng nếu chúng thực sự được sử dụng. – bdonlan

+0

Có - làm rõ giá trị. –

+0

Tôi không nghĩ rằng chúng là 'extern' - và ngay cả khi chúng có, thì phải có định nghĩa nào đó. Ctors/dtors của họ là một nguyên nhân (có thể là duy nhất) cho việc tăng kích thước thực thi. – sbi

9

file header là thường chỉ là các khai báo và không trực tiếp dẫn đến việc tạo mã máy. Trình liên kết đủ thông minh để không sử dụng các hàm không sử dụng từ CRT, vì vậy chỉ cần bao gồm stdio.h mà không sử dụng bất kỳ hàm nào của nó sẽ không dẫn đến nhiều mã trong tệp thực thi của bạn.

CHỈNH SỬA: Chúng có thể bao gồm các hàm, lớp, v.v. nội tuyến, bao gồm mã, nhưng chúng không làm tăng kích thước thực thi của bạn cho đến khi chúng thực sự được sử dụng.

+1

Đúng, nhưng điều này không giải thích tại sao '' làm tăng kích thước thực thi. – sbi

2

Thông thường, tệp tiêu đề chỉ chứa thông tin cho trình biên dịch chứ không phải mã thực. Ví dụ:

struct foo { 
    int x; 
}; 

Định nghĩa cấu trúc như thường xuất hiện trong tiêu đề, nhưng chúng không thực sự tạo mã, vì chúng chỉ cung cấp thông tin trình biên dịch về cách xử lý 'foo', một lát sau. Nếu nó không thấy foo, thông tin sẽ bị mất khi quá trình biên dịch kết thúc.

Thực tế, có bất kỳ thứ gì mà hiện tạo mã thường sẽ tạo ra lỗi. Ví dụ:

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

Nếu đây là trong một tiêu đề, và được bao gồm trong hai tập tin .c, các foo() chức năng sẽ được tạo hai lần. Điều này sẽ gây ra lỗi tại liên kết. Có thể tránh được lỗi nếu bạn có lý do chính đáng để làm như vậy, nhưng nói chung thực sự tạo mã trong tiêu đề là tránh được nếu có thể.

Lưu ý rằng một ngoại lệ ở đây là thành viên nội tuyến trong C++.Ví dụ:

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

Thanh nói kỹ thuật() được tạo trong mọi tệp bao gồm mã này. Trình biên dịch thực hiện các thủ thuật khác nhau để tránh lỗi (ràng buộc yếu, vv). Điều này thực sự có thể làm tăng kích thước của tệp thực thi và có thể là những gì bạn đã thấy với <iostream>.

3

Có một số khởi tạo tĩnh trong iostream, trong khi ở stdio.h chỉ có các hàm và định nghĩa của chúng. Do đó, bao gồm cả iostream sẽ tạo ra khả năng thực thi kích thước lớn hơn.

0

tệp iostream đã khai báo một số đối tượng toàn cầu:

std :: cout, std :: cerr, std :: cin là loại ostream.

Sau đó trình biên dịch sẽ đưa lớp đó và biên dịch nó ngay vào tệp nhị phân cuối cùng của bạn, thêm quá nhiều vào kích thước của nó.

1

Tiêu đề <iostream> đi kèm với một vài đối tượng, 'std :: cin , 'std::cout, std::cerrstd::clog. Đây là những trường hợp của các lớp có các hàm tạo và các trình phá hủy không tầm thường. Đây là mã và phải được liên kết. Đây là những gì làm tăng kích thước của tập tin thực thi.

AFAIK, <cstdio> không có mã, vì vậy đó là lý do tại sao không tăng kích thước thực thi.