2011-02-02 22 views
19

Tôi đi qua this question hỏi cách thực thi mã trước main() trong C, đề cập đến các chiến lược cho C++. Tôi đã chủ yếu sống trong không gian ứng dụng, vì vậy thực hiện trước khi main() chưa bao giờ xảy ra với tôi. Những thứ gì đòi hỏi kỹ thuật này?Loại hoạt động nào có thể cần thực hiện trước khi chính()

+1

Những điều không nên làm. –

+3

Điều này thật thú vị: http: // stackoverflow.com/questions/4783404/là-chính-thực sự-bắt đầu-of-a-c-chương trình – Dave

Trả lời

14

"Loại thứ gì yêu cầu kỹ thuật này?"

Điểm thực tế: không.

Tuy nhiên, có rất nhiều điều hữu ích bạn có thể muốn làm trước khi chính vì nhiều lý do. Chỉ với một ví dụ thực tế, giả sử bạn có một nhà máy trừu tượng xây dựng các doohickies. Bạn có thể chắc chắn để xây dựng trường hợp nhà máy, gán nó cho một số khu vực đặc biệt, và sau đó đăng ký các doohickies bê tông khác nhau để nó ... có, bạn có thể làm điều đó. Mặt khác, nếu bạn thực hiện nhà máy như một singleton và sử dụng sự kiện khởi tạo giá trị toàn cầu để "lừa" việc thực hiện đăng ký doohickies cụ thể trước khi bắt đầu chính bạn có được nhiều lợi ích với rất ít chi phí (thực tế của sử dụng singletons, về cơ bản không phải là vấn đề ở đây, là khá nhiều người duy nhất).

Ví dụ bạn:

  1. Đừng có để duy trì một danh sách đăng ký mà tất cả phải được gọi một cách rõ ràng. Trong thực tế, bạn thậm chí có thể khai báo và định nghĩa toàn bộ một lớp trong phạm vi riêng tư, ngoài tầm nhìn của bất kỳ ai và có sẵn để sử dụng khi chương trình bắt đầu.

  2. chính() không phải làm một loạt các crap với một loạt các đối tượng mà nó không quan tâm.

Vì vậy, không ai trong số này thực sự là cần thiết. Tuy nhiên, bạn có thể giảm các vấn đề về ghép nối và bảo trì nếu bạn tận dụng thực tế là các hình cầu được khởi tạo trước khi bắt đầu chính.

Chỉnh sửa:

Cần lưu ý rằng tôi đã biết rằng điều này không được ngôn ngữ đảm bảo.C++ chỉ đảm bảo rằng số không hoặc hằng số khởi chạy diễn ra trước chính. Những gì tôi nói về câu trả lời này là động khởi tạo. Việc bảo đảm C++ này xảy ra trước khi sử dụng biến đầu tiên, giống như các biến tĩnh cục bộ của hàm.

Mọi trình biên dịch mặc dù dường như khởi tạo động trước khi chính. Tôi nghĩ rằng tôi đã gặp một lần nhưng điều đó không đúng nhưng tôi tin rằng nguồn gốc của vấn đề là một thứ khác.

+0

+1, đây là sử dụng duy nhất tôi có cho điều này ... nhưng cậu bé để tôi sử dụng nó! –

5

Kỹ thuật này có thể được sử dụng cho các thường trình khởi tạo thư viện hoặc để khởi tạo dữ liệu sẽ được sử dụng ngầm trong khi thực hiện chương trình.


GCC cung cấp constructordestructorfunction attributes gây một chức năng để tự động được gọi trước khi thực hiện bước vào main() hoặc main() đã hoàn thành hoặc exit() đã được gọi là, tương ứng.

void __attribute__ ((constructor)) my_init(void); 
void __attribute__ ((destructor)) my_fini(void); 

Trong trường hợp khởi tạo thư viện, thói quen xây dựng được thực hiện trước khi dlopen() lợi nhuận nếu thư viện được nạp trong thời gian chạy hoặc trước main() được bắt đầu nếu thư viện được nạp tại thời gian tải. Khi được sử dụng để dọn dẹp thư viện, các thói quen hủy được thực thi trước khi trả về dlclose() nếu thư viện được tải vào thời gian chạy hoặc sau exit() hoặc hoàn thành main() nếu thư viện được tải vào thời gian tải.

1

Nếu bạn có thư viện, rất thuận tiện để có thể khởi tạo một số dữ liệu, tạo chuỗi v.v. trước khi hàm main() được gọi và biết rằng trạng thái mong muốn đạt được mà không cần phải gánh nặng và tin tưởng ứng dụng khách một cách rõ ràng gọi một số thư viện initialisation và/hoặc tắt mã. Bề ngoài, điều này có thể đạt được bằng cách có một đối tượng tĩnh có constructor và destructor thực hiện các hoạt động cần thiết. Thật không may, nhiều đối tượng tĩnh trong các đơn vị dịch thuật hoặc thư viện khác nhau sẽ có thứ tự khởi tạo không xác định, vì vậy nếu chúng phụ thuộc vào nhau (tệ hơn, theo kiểu cyclic), thì chúng vẫn chưa đạt được trạng thái khởi tạo của chúng trước khi yêu cầu đến Tương tự như vậy, một đối tượng tĩnh có thể tạo ra các luồng và các dịch vụ cuộc gọi trong một đối tượng khác chưa được tạo luồng. Vì vậy, một cách tiếp cận có cấu trúc hơn với trường hợp singleton thích hợp và ổ khóa là cần thiết cho sự vững mạnh khi đối mặt với việc sử dụng tùy ý, và toàn bộ điều có vẻ hấp dẫn hơn nhiều, mặc dù nó vẫn có thể là một chiến thắng ròng trong một số trường hợp.

3

Stuff thực hiện trước khi chính:

  • Trên x86, con trỏ thanh ghi chồng thường là & = 0XF3 để làm cho nó một bội số của 4 (alignment)
  • thành viên tĩnh được khởi tạo
  • đẩy argc và argv (và environ nếu cần)
  • gọi _main = p

g ++ 4.4 phát ra BEF sau quặng bất kỳ mã nào của tôi được phát ra. Về mặt kỹ thuật nó chèn nó vào đầu main trước khi bất kỳ mã của tôi, nhưng tôi đã nhìn thấy các trình biên dịch sử dụng _init thay vì _main như điểm mấu chốt:

.cfi_startproc 
.cfi_personality 0x3,__gxx_personality_v0 
pushq %rbp 
.cfi_def_cfa_offset 16 
movq %rsp, %rbp 
.cfi_offset 6, -16 
.cfi_def_cfa_register 6 
subq $16, %rsp 
movl %edi, -4(%rbp) 
movq %rsi, -16(%rbp) 
# My code follows 
3

Những điều duy nhất bạn có thể muốn làm trước khi main liên quan các biến toàn cầu, điều xấu, và những thứ tương tự luôn có thể được thực hiện bằng cách khởi tạo lười (khởi tạo tại điểm sử dụng đầu tiên). Tất nhiên họ sẽ được thực hiện tốt hơn nhiều bằng cách không sử dụng các biến toàn cục.

Một "ngoại lệ" có thể là khởi tạo các bảng hằng số chung khi chạy. Nhưng đây là một thực tế rất xấu, vì các bảng không thể chia sẻ giữa các cá thể của một thư viện/quá trình nếu bạn điền chúng vào thời gian chạy. Sẽ là thông minh hơn khi viết kịch bản để tạo các bảng static const dưới dạng tệp nguồn C hoặc C++ tại thời gian xây dựng.

2

Mọi thứ cần chạy mã để đảm bảo bất biến cho mã của bạn sau khi main bắt đầu chạy trước main. Những thứ như iostreams toàn cầu, thư viện thời gian chạy trên C, liên kết hệ điều hành, v.v.

Bây giờ bạn có thực sự cần viết mã thực hiện những điều như vậy hay không là những gì mọi người khác đang trả lời.

Các vấn đề liên quan