Kompilasi program C ++ melibatkan tiga langkah:
Preprocessing: preprocessor mengambil file kode sumber C ++ dan berkaitan dengan #include
s, #define
s dan arahan preprocessor lainnya. Output dari langkah ini adalah file C ++ "murni" tanpa arahan pra-prosesor.
Kompilasi: kompiler mengambil output pra-prosesor dan menghasilkan file objek darinya.
Menautkan: tautan mengambil file objek yang dihasilkan oleh kompiler dan menghasilkan pustaka atau file yang dapat dieksekusi.
Preprocessing
Preprocessor menangani arahan preprocessor , seperti #include
dan #define
. Ini agnostik dari sintaksis C ++, oleh karena itu harus digunakan dengan hati-hati.
Ia bekerja pada satu file sumber C ++ pada satu waktu dengan mengganti #include
arahan dengan konten dari masing-masing file (yang biasanya hanya deklarasi), melakukan penggantian makro ( #define
), dan memilih bagian teks yang berbeda tergantung dari #if
, #ifdef
dan #ifndef
arahan.
Preprocessor bekerja pada aliran token preprocessing. Substitusi makro didefinisikan sebagai mengganti token dengan token lain (operator ##
memungkinkan penggabungan dua token saat masuk akal).
Setelah semua ini, preprocessor menghasilkan output tunggal yang merupakan aliran token yang dihasilkan dari transformasi yang dijelaskan di atas. Itu juga menambahkan beberapa spidol khusus yang memberitahu kompiler dari mana setiap baris berasal sehingga dapat menggunakannya untuk menghasilkan pesan kesalahan yang masuk akal.
Beberapa kesalahan dapat dihasilkan pada tahap ini dengan penggunaan #if
dan #error
arahan yang cerdas .
Kompilasi
Langkah kompilasi dilakukan pada setiap output preprosesor. Compiler mem-parsing kode sumber C ++ murni (sekarang tanpa arahan preprocessor) dan mengubahnya menjadi kode assembly. Kemudian gunakan back-end yang mendasari (assembler dalam toolchain) yang merakit kode itu ke dalam kode mesin yang menghasilkan file biner aktual dalam beberapa format (ELF, COFF, a.out, ...). File objek ini berisi kode yang dikompilasi (dalam bentuk biner) dari simbol-simbol yang didefinisikan dalam input. Simbol dalam file objek disebut dengan nama.
File objek dapat merujuk ke simbol yang tidak didefinisikan. Ini adalah kasus ketika Anda menggunakan deklarasi, dan tidak memberikan definisi untuk itu. Compiler tidak keberatan dengan hal ini, dan dengan senang hati akan menghasilkan file objek selama kode sumbernya terbentuk dengan baik.
Compiler biasanya membiarkan Anda menghentikan kompilasi pada saat ini. Ini sangat berguna karena dengan itu Anda dapat mengkompilasi setiap file kode sumber secara terpisah. Keuntungan yang diberikan ini adalah Anda tidak perlu mengkompilasi ulang semuanya jika Anda hanya mengubah satu file.
File objek yang dihasilkan dapat dimasukkan ke dalam arsip khusus yang disebut perpustakaan statis, untuk digunakan kembali nanti.
Pada tahap ini dilaporkan kesalahan "reguler", seperti kesalahan sintaksis atau kesalahan resolusi kelebihan beban yang dilaporkan.
Menautkan
Tautan adalah yang menghasilkan keluaran kompilasi akhir dari file objek yang dihasilkan kompiler. Output ini bisa berupa pustaka bersama (atau dinamis) (dan meskipun namanya mirip, pustaka tersebut tidak memiliki banyak kesamaan dengan pustaka statis yang disebutkan sebelumnya) atau yang dapat dieksekusi.
Ini menautkan semua file objek dengan mengganti referensi ke simbol yang tidak terdefinisi dengan alamat yang benar. Masing-masing simbol ini dapat didefinisikan dalam file objek lain atau di perpustakaan. Jika mereka didefinisikan di perpustakaan selain dari perpustakaan standar, Anda perlu memberi tahu linker tentang mereka.
Pada tahap ini kesalahan yang paling umum adalah definisi yang hilang atau definisi duplikat. Yang pertama berarti bahwa definisi tidak ada (yaitu mereka tidak ditulis), atau bahwa file objek atau pustaka tempat mereka berada tidak diberikan kepada linker. Yang terakhir jelas: simbol yang sama didefinisikan dalam dua file objek atau pustaka yang berbeda.
Topik ini dibahas di CProgramming.com:
https://www.cprogramming.com/compilingandlinking.html
Inilah yang penulis tulis di sana:
sumber
Di depan standar:
sebuah unit terjemahan adalah kombinasi dari file sumber, header termasuk dan file sumber dikurangi garis sumber dilewati oleh bersyarat inklusi preprocessor direktif.
standar mendefinisikan 9 fase dalam terjemahan. Empat yang pertama berhubungan dengan preprocessing, tiga berikutnya adalah kompilasi, yang berikutnya adalah instantiation dari templat (menghasilkan unit instantiation ) dan yang terakhir adalah menghubungkan.
Dalam praktiknya fase kedelapan (instantiation of templates) sering dilakukan selama proses kompilasi tetapi beberapa kompiler menunda ke fase penghubung dan beberapa menyebar di keduanya.
sumber
Kurusnya adalah CPU memuat data dari alamat memori, menyimpan data ke alamat memori, dan menjalankan instruksi secara berurutan dari alamat memori, dengan beberapa lompatan kondisional dalam urutan instruksi yang diproses. Masing-masing dari tiga kategori instruksi ini melibatkan penghitungan alamat ke sel memori yang akan digunakan dalam instruksi mesin. Karena instruksi mesin adalah panjang variabel tergantung pada instruksi tertentu yang terlibat, dan karena kami merangkai panjang variabel mereka bersama-sama saat kami membangun kode mesin kami, ada dua langkah proses yang terlibat dalam menghitung dan membangun alamat apa pun.
Pertama kita meletakkan alokasi memori sebaik mungkin sebelum kita bisa tahu apa yang sebenarnya terjadi di setiap sel. Kami mencari tahu byte, atau kata-kata, atau apa pun yang membentuk instruksi dan literal serta data apa pun. Kami baru saja mulai mengalokasikan memori dan membangun nilai-nilai yang akan membuat program saat kami pergi, dan mencatat di mana saja kita perlu kembali dan memperbaiki alamat. Di tempat itu kami meletakkan boneka hanya pad lokasi sehingga kami dapat terus menghitung ukuran memori. Misalnya kode mesin pertama kami mungkin mengambil satu sel. Kode mesin berikutnya mungkin mengambil 3 sel, yang melibatkan satu sel kode mesin dan dua sel alamat. Sekarang pointer alamat kami adalah 4. Kita tahu apa yang terjadi di sel mesin, yang merupakan kode op, tetapi kita harus menunggu untuk menghitung apa yang terjadi di sel-sel alamat sampai kita tahu di mana data itu akan ditemukan, yaitu
Jika hanya ada satu file sumber kompiler secara teoritis dapat menghasilkan kode mesin yang sepenuhnya dapat dieksekusi tanpa linker. Dalam dua proses lulus, ini bisa menghitung semua alamat aktual untuk semua sel data yang dirujuk oleh setiap mesin memuat atau menyimpan instruksi. Dan itu bisa menghitung semua alamat absolut yang dirujuk oleh instruksi lompatan absolut. Ini adalah bagaimana kompiler yang lebih sederhana, seperti yang ada di Forth bekerja, tanpa tautan.
Linker adalah sesuatu yang memungkinkan blok kode dikompilasi secara terpisah. Ini dapat mempercepat proses keseluruhan kode bangunan, dan memungkinkan beberapa fleksibilitas dengan bagaimana blok kemudian digunakan, dengan kata lain mereka dapat dipindahkan di memori, misalnya menambahkan 1000 ke setiap alamat untuk menggeser blok dengan 1000 sel alamat.
Jadi apa yang dihasilkan oleh kompiler adalah kode mesin kasar yang belum sepenuhnya dibangun, tetapi ditata sedemikian sehingga kita mengetahui ukuran segalanya, dengan kata lain sehingga kita dapat mulai menghitung di mana semua alamat absolut akan berada. kompiler juga menampilkan daftar simbol yang merupakan pasangan nama / alamat. Simbol menghubungkan offset memori dalam kode mesin dalam modul dengan nama. Offset menjadi jarak absolut ke lokasi memori simbol dalam modul.
Di situlah kita sampai ke penghubung. Linker pertama-tama menampar semua blok kode mesin ini bersama-sama dari ujung ke ujung dan mencatat di mana masing-masing dimulai. Kemudian ia menghitung alamat yang akan diperbaiki dengan menambahkan offset relatif dalam sebuah modul dan posisi absolut modul dalam tata letak yang lebih besar.
Jelas saya sudah terlalu menyederhanakan ini sehingga Anda dapat mencoba untuk menangkapnya, dan saya sengaja tidak menggunakan jargon file objek, tabel simbol, dll yang bagi saya merupakan bagian dari kebingungan.
sumber
GCC mengkompilasi program C / C ++ menjadi executable dalam 4 langkah.
Sebagai contoh,
gcc -o hello hello.c
dilakukan sebagai berikut:1. Pra-pemrosesan
Preprocessing melalui GNU C Preprocessor (
cpp.exe
), yang mencakup header (#include
) dan memperluas makro (#define
).File perantara yang dihasilkan "hello.i" berisi kode sumber yang diperluas.
2. Kompilasi
Kompiler mengkompilasi kode sumber yang sudah diproses menjadi kode perakitan untuk prosesor tertentu.
Opsi -S menentukan untuk menghasilkan kode rakitan, bukan kode objek. File rakitan yang dihasilkan adalah "hello.s".
3. Majelis
Assembler (
as.exe
) mengubah kode rakitan menjadi kode mesin di file objek "hello.o".4. Tautan
Akhirnya, linker (
ld.exe
) menautkan kode objek dengan kode perpustakaan untuk menghasilkan file yang dapat dieksekusi "halo".sumber
Lihatlah URL: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
Proses kompilasi lengkap C ++ diperkenalkan dengan jelas di URL ini.
sumber