Inisialisasi CRT
Artikel ini menjelaskan bagaimana CRT menginisialisasi status global dalam kode asli.
Secara default, linker menyertakan pustaka CRT, yang menyediakan kode startup-nya sendiri. Kode startup ini menginisialisasi pustaka CRT, memanggil inisialisasi global, lalu memanggil fungsi yang disediakan main
pengguna untuk aplikasi konsol.
Dimungkinkan, meskipun tidak disarankan, untuk memanfaatkan perilaku tautan khusus Microsoft untuk menyisipkan inisialisasi global Anda sendiri dalam urutan tertentu. Kode ini tidak portabel, dan dilengkapi dengan beberapa peringatan penting.
Menginisialisasi objek global
Pertimbangkan kode C++ berikut (C tidak akan mengizinkan kode ini karena tidak mengizinkan panggilan fungsi dalam ekspresi konstan).
int func(void)
{
return 3;
}
int gi = func();
int main()
{
return gi;
}
Menurut standar C/C++, func()
harus dipanggil sebelum main()
dijalankan. Tapi siapa yang menyebutnya?
Salah satu cara untuk menentukan pemanggil adalah dengan mengatur titik henti di func()
, men-debug aplikasi, dan memeriksa tumpukan. Dimungkinkan karena kode sumber CRT disertakan dengan Visual Studio.
Saat menelusuri fungsi pada tumpukan, Anda akan melihat bahwa CRT memanggil daftar penunjuk fungsi. Fungsi-fungsi ini mirip func()
dengan , atau konstruktor untuk instans kelas.
CRT mendapatkan daftar penunjuk fungsi dari pengkompilasi Microsoft C++. Ketika pengkompilasi melihat penginisialisasi global, penginisialisasi ini menghasilkan penginisialisasi dinamis di .CRT$XCU
bagian di mana CRT
adalah nama bagian dan XCU
merupakan nama grup. Untuk mendapatkan daftar penginisialisasi dinamis, jalankan perintah dumpbin /all main.obj
, lalu cari bagian .CRT$XCU
. Perintah hanya berlaku ketika main.cpp
dikompilasi sebagai file C++, bukan file C. Ini harus mirip dengan contoh ini:
SECTION HEADER #6
.CRT$XCU name
0 physical address
0 virtual address
4 size of raw data
1F2 file pointer to raw data (000001F2 to 000001F5)
1F6 file pointer to relocation table
0 file pointer to line numbers
1 number of relocations
0 number of line numbers
40300040 flags
Initialized Data
4 byte align
Read Only
RAW DATA #6
00000000: 00 00 00 00 ....
RELOCATIONS #6
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- -------
00000000 DIR32 00000000 C ??__Egi@@YAXXZ (void __cdecl `dynamic initializer for 'gi''(void))
CRT mendefinisikan dua pointer:
__xc_a
di.CRT$XCA
__xc_z
di.CRT$XCZ
Tidak ada grup yang memiliki simbol lain yang ditentukan kecuali __xc_a
dan __xc_z
.
Sekarang, ketika linker membaca berbagai .CRT
subbagian (bagian setelah $
), itu menggabungkannya dalam satu bagian dan mengurutkannya menurut abjad. Ini berarti bahwa penginisialisasi global yang ditentukan pengguna (yang dimasukkan .CRT$XCU
pengkompilasi Microsoft C++ ) selalu datang setelah .CRT$XCA
dan sebelum .CRT$XCZ
.
Bagian harus menyerupai contoh ini:
.CRT$XCA
__xc_a
.CRT$XCU
Pointer to Global Initializer 1
Pointer to Global Initializer 2
.CRT$XCZ
__xc_z
Pustaka CRT menggunakan dan __xc_a
__xc_z
untuk menentukan awal dan akhir daftar inisialisasi global karena cara mereka diletakkan dalam memori setelah gambar dimuat.
Fitur linker untuk inisialisasi
Standar C++ tidak menyediakan cara yang sesuai untuk menentukan urutan relatif di seluruh unit terjemahan untuk inisialisasi global yang disediakan pengguna. Namun, karena linker Microsoft memesan .CRT
subbagian menurut abjad, dimungkinkan untuk memanfaatkan urutan ini untuk menentukan urutan inisialisasi. Kami tidak merekomendasikan teknik khusus Microsoft ini, dan mungkin rusak dalam rilis mendatang. Kami telah mendokumenkannya hanya untuk mencegah Anda membuat kode yang rusak dengan cara yang sulit didiagnosis.
Untuk membantu mencegah masalah dalam kode Anda, dimulai di Visual Studio 2019 versi 16.11, kami telah menambahkan dua hal baru secara default : C5247 dan C5248. Aktifkan peringatan ini untuk mendeteksi masalah saat membuat inisialisasi Anda sendiri.
Anda dapat menambahkan penginisialisasi ke nama bagian yang dipesan yang tidak digunakan untuk membuatnya dalam urutan relatif tertentu ke penginisialisasi dinamis yang dihasilkan kompilator:
#pragma section(".CRT$XCT", read)
// 'i1' is guaranteed to be called before any compiler generated C++ dynamic initializer
__declspec(allocate(".CRT$XCT")) type i1 = f;
#pragma section(".CRT$XCV", read)
// 'i2' is guaranteed to be called after any compiler generated C++ dynamic initializer
__declspec(allocate(".CRT$XCV")) type i2 = f;
Nama .CRT$XCT
dan .CRT$XCV
tidak digunakan oleh pengkompilasi atau pustaka CRT saat ini, tetapi tidak ada jaminan bahwa mereka akan tetap tidak digunakan di masa depan. Dan, variabel Anda masih dapat dioptimalkan jauh oleh pengkompilasi. Pertimbangkan potensi masalah rekayasa, pemeliharaan, dan portabilitas sebelum mengadopsi teknik ini.
Lihat juga
_initterm, _initterm_e
File runtime C (CRT) dan C++ Standard Library (STL) .lib