Складання програми C ++ включає три етапи:
Попередня обробка: препроцесор бере файл вихідного коду C ++ і має справу з директивами #include
s, #define
s та іншими препроцесорами. Результатом цього кроку є "чистий" C ++ файл без директив попереднього процесора.
Компіляція: компілятор бере висновок попереднього процесора і виробляє з нього об’єктний файл.
Зв'язування: лінкер бере файли об'єктів, створені компілятором, і створює або бібліотеку, або виконуваний файл.
Попередня обробка
Препроцесор обробляє директиви препроцесора , як #include
і #define
. Це агностик синтаксису C ++, тому його потрібно використовувати обережно.
Він працює на одному C ++ вихідного файлу , в той час, замінивши #include
директиви з вмістом відповідних файлів (які, як правило , тільки декларація), роблячи заміну макросів ( #define
), і вибираючи різні частини тексту , в залежності від #if
, #ifdef
і #ifndef
директиви.
Препроцесор працює над потоком маркерів попередньої обробки. Заміна макросів визначається як заміна лексем на інші лексеми (оператор ##
дозволяє об'єднати два лексеми, коли це має сенс).
Після всього цього препроцесор виробляє єдиний вихід, який представляє собою потік лексем, що є результатом перетворень, описаних вище. Він також додає деякі спеціальні маркери, які повідомляють компілятору, звідки походить кожен рядок, щоб він міг використовувати їх для створення розважливих повідомлень про помилки.
На цьому етапі можуть бути допущені деякі помилки при розумному використанні директив #if
та #error
директив.
Компіляція
Етап компіляції виконується на кожному виході препроцесора. Компілятор аналізує чистий вихідний код C ++ (тепер без будь-яких директив препроцесора) і перетворює його в код складання. Потім викликає базовий бек-енд (асемблер в ланцюжку інструментів), який збирає цей код у машинний код, створюючи фактичний бінарний файл у якомусь форматі (ELF, COFF, a.out, ...). Цей об'єктний файл містить скомпільований код (у двійковій формі) символів, визначених на вході. Символи в об'єктних файлах посилаються на ім'я.
Файли об'єктів можуть посилатися на символи, які не визначені. Це той випадок, коли ви використовуєте декларацію, а не надаєте визначення для неї. Компілятор цього не заперечує, і він із задоволенням видасть об’єктний файл, доки вихідний код буде добре сформований.
Зазвичай компілятори дозволяють зупинити компіляцію на цьому етапі. Це дуже корисно, оскільки за допомогою нього ви можете зібрати кожен файл вихідного коду окремо. Перевага, яке надає, полягає в тому, що вам не потрібно перекомпілювати все, якщо ви зміните лише один файл.
Отримані файли об'єктів можуть бути поміщені в спеціальні архіви, які називаються статичними бібліотеками, для подальшого подальшого використання.
Саме на цьому етапі повідомляються про "звичайні" помилки компілятора, такі як синтаксичні помилки або помилки роздільної помилки.
Зв’язування
Лінкер - це те, що виробляє кінцевий вихід компіляції з файлів об'єктів, які виробляв компілятор. Цей вихід може бути або спільною (або динамічною) бібліотекою (і хоча назва схожа, вони не мають багато спільного зі статичними бібліотеками, згаданими раніше), або виконувані файли.
Він пов'язує всі файли об'єктів, замінюючи посилання на невизначені символи правильними адресами. Кожен з цих символів може бути визначений в інших об’єктних файлах або в бібліотеках. Якщо вони визначені в інших бібліотеках, ніж у стандартній бібліотеці, вам потрібно повідомити про них лінкер.
На цьому етапі найпоширенішими помилками є відсутні дефініції або дублюючі визначення. Перший означає, що або визначення не існують (тобто вони не записуються), або що об'єктні файли або бібліотеки, де вони мешкають, не були надані лінкеру. Останнє очевидно: той самий символ був визначений у двох різних файлах об'єктів або бібліотеках.