กระบวนการรวบรวม / เชื่อมโยงทำงานอย่างไร


416

กระบวนการรวบรวมและเชื่อมโยงทำงานอย่างไร

(หมายเหตุ: นี่คือหมายถึงการเป็นรายการที่จะกองมากเกินของ C ++ คำถามที่พบบ่อย . ถ้าคุณต้องการที่จะวิจารณ์ความคิดของการให้คำถามที่พบบ่อยในรูปแบบนี้แล้วโพสต์บนเมตาที่เริ่มต้นทั้งหมดนี้ . จะเป็นสถานที่ที่จะทำเช่นนั้นได้คำตอบ คำถามนั้นได้รับการตรวจสอบในห้องสนทนา C ++ซึ่งแนวคิด FAQ เริ่มต้นตั้งแต่แรกดังนั้นคำตอบของคุณมีโอกาสมากที่จะได้รับการอ่านโดยผู้ที่คิดแนวคิดนี้)

คำตอบ:


554

การรวบรวมโปรแกรม C ++ เกี่ยวข้องกับสามขั้นตอน:

  1. การประมวลผลล่วงหน้า: ตัวประมวลผลล่วงหน้าใช้ไฟล์ซอร์สโค้ด C ++ และเกี่ยวข้องกับคำสั่ง#includes, #defines และตัวประมวลผลล่วงหน้าอื่น ๆ ผลลัพธ์ของขั้นตอนนี้คือไฟล์ "บริสุทธิ์" C ++ ที่ไม่มีคำสั่งพรีโปรเซสเซอร์

  2. การคอมไพล์: คอมไพเลอร์ใช้เอาต์พุตของตัวประมวลผลล่วงหน้าและสร้างวัตถุไฟล์จากมัน

  3. การลิงก์: ตัวลิงก์ใช้ไฟล์อ็อบเจ็กต์ที่สร้างโดยคอมไพเลอร์และสร้างไลบรารีหรือไฟล์ที่เรียกทำงานได้

กระบวนการเตรียมการผลิต

พรีโพรเซสเซอร์จัดการสั่ง preprocessorเช่นและ#include #defineมันเป็นผู้ไม่เชื่อเรื่องพระเจ้าของไวยากรณ์ของ C ++ ซึ่งเป็นเหตุผลที่จะต้องใช้ด้วยความระมัดระวัง

มันทำงานบนไฟล์ต้นฉบับ C ++ ครั้งละหนึ่งไฟล์โดยแทนที่#includeคำสั่งด้วยเนื้อหาของไฟล์ที่เกี่ยวข้อง (ซึ่งมักจะเป็นเพียงการประกาศ) ทำการแทนที่มาโคร ( #define) และเลือกส่วนต่าง ๆ ของข้อความ#ifตาม#ifdefและ#ifndefคำสั่ง

ตัวประมวลผลล่วงหน้าทำงานบนสตรีมของโทเค็นการประมวลผลล่วงหน้า การแทนที่แมโครถูกกำหนดเป็นการแทนที่โทเค็นด้วยโทเค็นอื่น ๆ (ตัวดำเนินการ##ช่วยให้สามารถรวมโทเค็นสองตัวเมื่อมีความหมาย)

หลังจากทั้งหมดนี้ตัวประมวลผลล่วงหน้าสร้างเอาต์พุตเดียวที่เป็นสตรีมของโทเค็นที่เป็นผลมาจากการแปลงที่อธิบายข้างต้น นอกจากนี้ยังเพิ่มเครื่องหมายพิเศษบางอย่างที่บอกคอมไพเลอร์ว่าแต่ละบรรทัดมาจากไหนเพื่อให้สามารถใช้ข้อความเหล่านั้นเพื่อสร้างข้อความแสดงข้อผิดพลาดที่สมเหตุสมผล

ข้อผิดพลาดบางอย่างสามารถเกิดขึ้นได้ในขั้นตอนนี้ด้วยการใช้คำสั่ง#ifและ#errorคำสั่งอย่างชาญฉลาด

การรวบรวม

ขั้นตอนการรวบรวมจะดำเนินการในแต่ละเอาต์พุตของตัวประมวลผลล่วงหน้า คอมไพเลอร์แยกวิเคราะห์รหัสที่มาบริสุทธิ์ C ++ (ตอนนี้ไม่มีคำสั่ง preprocessor ใด ๆ ) และแปลงเป็นรหัสประกอบ จากนั้นเรียกใช้ back-end พื้นฐาน (แอสเซมเบลอร์ใน toolchain) ที่ประกอบรหัสนั้นลงในรหัสเครื่องที่สร้างไฟล์ไบนารีจริงในบางรูปแบบ (ELF, COFF, a.out, ... ) ไฟล์อ็อบเจ็กต์นี้มีโค้ดที่คอมไพล์แล้ว (ในรูปแบบไบนารี) ของสัญลักษณ์ที่กำหนดในอินพุต สัญลักษณ์ในอ็อบเจ็กต์ไฟล์ถูกอ้างอิงโดยชื่อ

ไฟล์อ็อบเจ็กต์สามารถอ้างถึงสัญลักษณ์ที่ไม่ได้กำหนดไว้ นี่เป็นกรณีเมื่อคุณใช้การประกาศและไม่ได้ให้คำจำกัดความใด ๆ คอมไพเลอร์ไม่ได้สนใจสิ่งนี้และจะสร้างไฟล์ออบเจ็กต์อย่างมีความสุขตราบเท่าที่ซอร์สโค้ดมีรูปแบบที่ดี

คอมไพเลอร์มักจะให้คุณหยุดการรวบรวม ณ จุดนี้ สิ่งนี้มีประโยชน์มากเพราะคุณสามารถรวบรวมไฟล์โค้ดแต่ละไฟล์แยกกันได้ ข้อได้เปรียบที่มีให้คือคุณไม่จำเป็นต้องคอมไพล์ทุกสิ่งใหม่หากคุณเปลี่ยนไฟล์เพียงไฟล์เดียว

ไฟล์อ็อบเจ็กต์ที่สร้างสามารถใส่ในไฟล์เก็บถาวรพิเศษที่เรียกว่าสแตติกไลบรารีเพื่อให้นำกลับมาใช้ใหม่ได้ง่ายขึ้นในภายหลัง

ในขั้นตอนนี้จะมีการรายงานข้อผิดพลาดของคอมไพเลอร์ "ปกติ" เช่นข้อผิดพลาดทางไวยากรณ์หรือข้อผิดพลาดในการแก้ปัญหาโอเวอร์โหลดที่ไม่ถูกต้อง

การเชื่อมโยง

ตัวลิงก์คือสิ่งที่สร้างเอาต์พุตการคอมไพล์สุดท้ายจากอ็อบเจ็กต์ไฟล์ที่คอมไพเลอร์สร้างขึ้น เอาต์พุตนี้สามารถเป็นไลบรารีแบบแบ่งใช้ (หรือแบบไดนามิก) (และแม้ว่าชื่อจะคล้ายกัน แต่ก็ไม่มีอะไรเหมือนกันกับไลบรารีแบบคงที่ที่กล่าวถึงก่อนหน้านี้) หรือไฟล์ที่เรียกใช้งานได้

มันเชื่อมโยงไฟล์วัตถุทั้งหมดโดยแทนที่การอ้างอิงถึงสัญลักษณ์ที่ไม่ได้กำหนดด้วยที่อยู่ที่ถูกต้อง สัญลักษณ์เหล่านี้แต่ละตัวสามารถกำหนดได้ในไฟล์วัตถุอื่นหรือในไลบรารี หากมีการกำหนดไว้ในไลบรารีอื่นนอกเหนือจากไลบรารีมาตรฐานคุณจะต้องบอก linker เกี่ยวกับพวกเขา

ในขั้นตอนนี้ข้อผิดพลาดที่พบบ่อยที่สุดจะหายไปคำจำกัดความหรือคำนิยามที่ซ้ำกัน ก่อนหน้านี้หมายถึงว่าไม่มีคำจำกัดความ (เช่นพวกเขาไม่ได้เขียน) หรือว่าวัตถุไฟล์หรือห้องสมุดที่พวกเขาอาศัยอยู่ไม่ได้รับการ linker ชัดเจนหลัง: สัญลักษณ์เดียวกันถูกกำหนดในสองไฟล์อ็อบเจ็กต์หรือไลบรารีที่แตกต่างกัน


39
ขั้นตอนการรวบรวมจะเรียกแอสเซมเบลอร์ก่อนที่จะแปลงเป็นวัตถุไฟล์
manav mn

3
ใช้การเพิ่มประสิทธิภาพที่ไหน จากภาพรวมครั้งแรกดูเหมือนว่าจะทำได้ในขั้นตอนการรวบรวม แต่ในทางกลับกันฉันสามารถจินตนาการได้ว่าการเพิ่มประสิทธิภาพที่เหมาะสมสามารถทำได้หลังจากเชื่อมโยงเท่านั้น
Bart van Heukelom

6
@BartvanHeukelom ตามเนื้อผ้ามันทำในระหว่างการคอมไพล์ แต่คอมไพเลอร์สมัยใหม่สนับสนุนการเรียกว่า "การเพิ่มประสิทธิภาพลิงค์เวลา" ซึ่งมีข้อได้เปรียบในการเพิ่มประสิทธิภาพของหน่วยการแปล
R. Martinho Fernandes

3
C มีขั้นตอนเดียวกันหรือไม่
Kevin Zhu

6
หาก linker แปลงสัญลักษณ์ที่อ้างถึงคลาส / วิธีการในไลบรารีไปยังที่อยู่นั่นหมายความว่าไบนารีไลบรารีจะถูกเก็บไว้ในที่อยู่หน่วยความจำที่ระบบปฏิบัติการคงที่หรือไม่ ฉันแค่สับสนว่า linker จะรู้ที่อยู่ที่แน่นอนของพูด stdio ไบนารีสำหรับระบบเป้าหมายทั้งหมด เส้นทางของไฟล์จะเหมือนเดิมเสมอ แต่ที่อยู่ที่แน่นอนสามารถเปลี่ยนแปลงได้ใช่ไหม
Dan Carter

42

หัวข้อนี้จะกล่าวถึงที่ CProgramming.com:
https://www.cprogramming.com/compilingandlinking.html

นี่คือสิ่งที่ผู้เขียนเขียนไว้:

การรวบรวมไม่เหมือนกับการสร้างไฟล์ที่เรียกใช้งานได้! แต่การสร้างไฟล์ที่เรียกทำงานได้นั้นเป็นกระบวนการหลายขั้นตอนซึ่งแบ่งออกเป็นสององค์ประกอบ: การคอมไพล์และการลิงก์ ในความเป็นจริงแม้ว่าโปรแกรม "คอมไพล์ดี" มันอาจไม่ทำงานจริงเนื่องจากข้อผิดพลาดในระหว่างขั้นตอนการเชื่อมโยง กระบวนการทั้งหมดในการดำเนินการจากซอร์สโค้ดไฟล์ไปยังไฟล์ที่เรียกทำงานได้อาจเรียกได้ว่าบิลด์

การรวบรวม

การคอมไพล์หมายถึงการประมวลผลไฟล์ซอร์สโค้ด (.c, .cc หรือ. cpp) และการสร้างไฟล์ 'object' ขั้นตอนนี้ไม่ได้สร้างสิ่งใด ๆ ที่ผู้ใช้สามารถเรียกใช้ได้ คอมไพเลอร์เพียง แต่สร้างคำสั่งภาษาเครื่องที่สอดคล้องกับไฟล์รหัสต้นฉบับที่รวบรวม ตัวอย่างเช่นหากคุณรวบรวม (แต่อย่าเชื่อมโยง) สามไฟล์แยกกันคุณจะมีสามวัตถุไฟล์ที่สร้างขึ้นเป็นเอาท์พุทแต่ละคนมีชื่อ. o หรือ. obj (นามสกุลจะขึ้นอยู่กับคอมไพเลอร์ของคุณ) ไฟล์เหล่านี้แต่ละไฟล์มีการแปลไฟล์ซอร์สโค้ดของคุณเป็นไฟล์ภาษาเครื่อง แต่คุณยังไม่สามารถรันได้! คุณต้องเปลี่ยนมันให้เป็นไฟล์ปฏิบัติการที่ระบบปฏิบัติการของคุณสามารถใช้ได้ นั่นคือสิ่งที่นักเชื่อมโยงเข้ามา

การเชื่อมโยง

การลิงก์หมายถึงการสร้างไฟล์เรียกทำงานไฟล์เดียวจากหลาย ๆ ออบเจ็กต์ไฟล์ ในขั้นตอนนี้เป็นเรื่องปกติที่ตัวเชื่อมโยงจะบ่นเกี่ยวกับฟังก์ชั่นที่ไม่ได้กำหนด (โดยทั่วไปคือตัวหลักเอง) ในระหว่างการรวบรวมถ้าคอมไพเลอร์ไม่สามารถหาคำนิยามสำหรับฟังก์ชั่นที่เฉพาะเจาะจงก็จะถือว่าฟังก์ชั่นที่ถูกกำหนดไว้ในไฟล์อื่น หากไม่ใช่กรณีนี้จะไม่มีทางที่คอมไพเลอร์จะรู้ - มันจะไม่ดูเนื้อหามากกว่าหนึ่งไฟล์ในแต่ละครั้ง เครื่องมือเชื่อมโยงอาจดูไฟล์หลาย ๆ ไฟล์และพยายามค้นหาการอ้างอิงสำหรับฟังก์ชั่นที่ไม่ได้กล่าวถึง

คุณอาจถามว่าทำไมมีขั้นตอนการรวบรวมและเชื่อมโยงแยกกัน อย่างแรกมันอาจจะง่ายกว่าที่จะนำสิ่งต่าง ๆ ไปใช้ คอมไพเลอร์ทำสิ่งที่มันและตัวเชื่อมโยงทำสิ่งนั้นโดยแยกฟังก์ชั่นออกทำให้ความซับซ้อนของโปรแกรมลดลง ข้อดีอีกอย่าง (ชัดเจน) คือการอนุญาตให้สร้างโปรแกรมขนาดใหญ่โดยไม่ต้องทำขั้นตอนการคอมไพล์ใหม่ทุกครั้งที่มีการเปลี่ยนแปลงไฟล์ "การรวบรวมตามเงื่อนไข" ดังนั้นจึงจำเป็นต้องรวบรวมเฉพาะไฟล์ต้นฉบับที่มีการเปลี่ยนแปลง สำหรับส่วนที่เหลือไฟล์อ็อบเจ็กต์คืออินพุตที่เพียงพอสำหรับตัวลิงก์ ในที่สุดสิ่งนี้ทำให้ง่ายต่อการใช้ไลบรารี่ของโค้ดที่คอมไพล์แล้ว: เพียงแค่สร้างออบเจ็กต์ไฟล์และเชื่อมโยงเข้ากับไฟล์ออบเจ็กต์อื่น ๆ

เพื่อให้ได้รับประโยชน์อย่างเต็มที่จากการคอมไพล์เงื่อนไขคุณอาจได้รับโปรแกรมที่จะช่วยคุณได้ง่ายกว่าการลองและจดจำว่าไฟล์ใดที่คุณเปลี่ยนไปตั้งแต่คุณคอมไพล์ครั้งล่าสุด (แน่นอนคุณสามารถคอมไพล์ไฟล์ทุกไฟล์ที่มีการประทับเวลามากกว่าเวลาของไฟล์ออบเจกต์ที่เกี่ยวข้อง) หากคุณกำลังทำงานกับสภาพแวดล้อมการพัฒนาแบบรวม (IDE) มันอาจดูแลเรื่องนี้ให้คุณแล้ว หากคุณใช้เครื่องมือบรรทัดคำสั่งมียูทิลิตีที่ดีที่เรียกว่า make ซึ่งมาพร้อมกับการกระจาย * nix ส่วนใหญ่ นอกเหนือจากการคอมไพล์แบบมีเงื่อนไขแล้วยังมีฟีเจอร์ที่น่าสนใจอื่น ๆ อีกมากมายสำหรับการเขียนโปรแกรมเช่นการอนุญาตให้รวบรวมโปรแกรมที่แตกต่างกันของคุณ - ตัวอย่างเช่นถ้าคุณมีเวอร์ชันที่สร้าง verbose output สำหรับการดีบัก

การทราบความแตกต่างระหว่างเฟสการคอมไพล์และเฟสลิงก์สามารถทำให้ง่ายต่อการค้นหาข้อบกพร่อง ข้อผิดพลาดของคอมไพเลอร์มักจะอยู่ในรูปแบบวากยสัมพันธ์ - เซมิโคลอนที่หายไปซึ่งเป็นเครื่องหมายวงเล็บพิเศษ การเชื่อมโยงข้อผิดพลาดมักจะเกี่ยวข้องกับการขาดหายไปหรือหลายคำจำกัดความ หากคุณได้รับข้อผิดพลาดว่ามีการกำหนดฟังก์ชันหรือตัวแปรหลายครั้งจากตัวเชื่อมโยงนั่นเป็นการบ่งชี้ที่ดีว่าข้อผิดพลาดคือไฟล์ซอร์สโค้ดสองไฟล์ของคุณมีฟังก์ชันหรือตัวแปรเดียวกัน


1
สิ่งที่ฉันไม่เข้าใจก็คือหากตัวประมวลผลล่วงหน้าจัดการสิ่งต่าง ๆ เช่น #includes เพื่อสร้างไฟล์ super หนึ่งไฟล์แล้วแน่นอนว่าไม่มีอะไรให้เชื่อมโยงหลังจากนั้น
binarysmacker

@binarysmacer ดูว่าสิ่งที่ฉันเขียนด้านล่างทำให้รู้สึกถึงคุณใด ๆ ฉันพยายามอธิบายปัญหาจากภายในสู่ภายนอก
มุมมองรูปไข่

3
@binarysmacker มันสายเกินไปที่จะแสดงความคิดเห็น แต่คนอื่น ๆ อาจพบว่ามีประโยชน์ youtu.be/D0TazQIkc8Qโดยทั่วไปคุณมีไฟล์ส่วนหัวและโดยทั่วไปแล้วไฟล์ส่วนหัวเหล่านี้มีเพียงการประกาศตัวแปร / ฟังก์ชั่นและไม่มีคำจำกัดความคำจำกัดความอาจมีอยู่ในไฟล์ต้นฉบับที่แยกต่างหากดังนั้นตัวประมวลผลล่วงหน้าจึงรวมถึงการประกาศเท่านั้น linker ช่วยคุณเชื่อมโยงไฟล์ต้นฉบับที่ใช้ตัวแปร / ฟังก์ชั่นกับไฟล์ต้นฉบับที่กำหนดไว้
Karan Joisher

24

ที่ด้านหน้ามาตรฐาน:

  • หน่วยการแปลคือการรวมกันของไฟล์ที่มาส่วนหัวและรวมไฟล์ที่มาน้อยเส้นแหล่งที่มาใด ๆ โดยข้ามเงื่อนไขสั่งรวม preprocessor

  • มาตรฐานกำหนด 9 ขั้นตอนในการแปล สี่รายการแรกนั้นสอดคล้องกับการประมวลผลล่วงหน้าสามรายการถัดไปคือการรวบรวมรายการถัดไปคือการสร้างอินสแตนซ์ของเทมเพลต (การสร้างหน่วยการสร้างอินสแตนซ์ ) และอันสุดท้ายเป็นการเชื่อมโยง

ในทางปฏิบัติขั้นตอนที่แปด (การสร้างอินสแตนซ์ของเทมเพลต) มักจะทำในระหว่างกระบวนการรวบรวม แต่คอมไพเลอร์บางตัวจะหน่วงเวลาไปยังขั้นตอนการเชื่อมโยงและบางตัวแพร่กระจายในทั้งสอง


14
คุณช่วยรายการทั้งหมด 9 ขั้นตอนได้ไหม นั่นเป็นส่วนเสริมที่ดีของคำตอบฉันคิดว่า :)
jalf

@jalf: ที่เกี่ยวข้อง: stackoverflow.com/questions/1476892/...
sbi

@jalf เพียงเพิ่มการสร้างอินสแตนซ์ของเทมเพลตก่อนขั้นตอนสุดท้ายในคำตอบที่ชี้โดย @sbi IIRC มีความแตกต่างเล็กน้อยในการใช้ถ้อยคำที่แม่นยำในการจัดการตัวละครที่กว้าง แต่ฉันไม่คิดว่ามันจะปรากฏในฉลากไดอะแกรม
AProgrammer

2
@sbi ใช่ แต่นี่ควรจะเป็นคำถามที่พบบ่อยใช่ไหม? ดังนั้นข้อมูลนี้จะไม่สามารถใช้ได้ที่นี่ ? ;)
jalf

3
@AProgrammmer: เพียงแค่ระบุชื่อพวกเขาด้วยชื่อก็จะเป็นประโยชน์ จากนั้นผู้คนจะรู้ว่าต้องค้นหาอะไรหากต้องการรายละเอียดเพิ่มเติม อย่างไรก็ตาม +1 คำตอบของคุณในกรณีใด ๆ :)
jalf

14

ผอมคือ CPU โหลดข้อมูลจากที่อยู่หน่วยความจำเก็บข้อมูลไปยังที่อยู่หน่วยความจำและดำเนินการคำแนะนำตามลำดับออกจากที่อยู่หน่วยความจำด้วยการกระโดดตามเงื่อนไขบางอย่างในลำดับของคำแนะนำการประมวลผล คำแนะนำทั้งสามประเภทเหล่านี้เกี่ยวข้องกับการคำนวณที่อยู่ไปยังเซลล์หน่วยความจำที่จะใช้ในการเรียนการสอนของเครื่อง เนื่องจากคำสั่งเครื่องมีความยาวผันแปรขึ้นอยู่กับคำสั่งเฉพาะที่เกี่ยวข้องและเนื่องจากเรารวบรวมความยาวของตัวแปรเข้าด้วยกันเมื่อเราสร้างรหัสเครื่องของเราจึงมีกระบวนการสองขั้นตอนที่เกี่ยวข้องในการคำนวณและสร้างที่อยู่ใด ๆ

ครั้งแรกที่เราจัดโครงสร้างการจัดสรรหน่วยความจำให้ดีที่สุดเท่าที่เราจะทำได้ก่อนที่เราจะรู้ว่าอะไรจะเกิดขึ้นในแต่ละเซลล์ เราหาจำนวนไบต์หรือคำหรืออะไรก็ตามที่สร้างคำแนะนำและตัวอักษรและข้อมูลใด ๆ เราเพิ่งเริ่มจัดสรรหน่วยความจำและสร้างค่าที่จะสร้างโปรแกรมในขณะที่เราไปและจดบันทึกทุกที่ที่เราต้องการย้อนกลับและแก้ไขที่อยู่ ในสถานที่นั้นเราใส่หุ่นจำลองเพื่อวางตำแหน่งเพื่อให้เราสามารถคำนวณขนาดหน่วยความจำต่อไป ตัวอย่างเช่นรหัสเครื่องแรกของเราอาจใช้เซลล์เดียว รหัสเครื่องถัดไปอาจใช้เวลา 3 เซลล์ซึ่งเกี่ยวข้องกับเซลล์รหัสเครื่องหนึ่งเซลล์และที่อยู่สองเซลล์ ตอนนี้ตัวชี้ที่อยู่ของเราคือ 4 เรารู้ว่าเกิดอะไรขึ้นในเซลล์ของเครื่องซึ่งเป็นรหัส op แต่เราต้องรอเพื่อคำนวณสิ่งที่ไปในเซลล์ที่อยู่จนกว่าเราจะรู้ว่าข้อมูลนั้นจะอยู่ที่ใด

หากมีไฟล์ต้นฉบับเพียงไฟล์เดียวคอมไพเลอร์สามารถสร้างโค้ดของเครื่องที่ปฏิบัติการได้อย่างสมบูรณ์โดยไม่มี linker ในกระบวนการส่งข้อมูลสองครั้งมันสามารถคำนวณที่อยู่จริงทั้งหมดไปยังเซลล์ข้อมูลทั้งหมดที่อ้างอิงโดยเครื่องโหลดหรือคำแนะนำการจัดเก็บ และสามารถคำนวณที่อยู่สัมบูรณ์ทั้งหมดที่อ้างอิงโดยคำแนะนำการกระโดดแบบสัมบูรณ์ นี่คือคอมไพเลอร์ที่เรียบง่ายเช่นเดียวกับที่ทำงานใน Forth โดยไม่มีตัวเชื่อมโยง

ตัวเชื่อมโยงคือสิ่งที่อนุญาตให้กลุ่มของรหัสที่จะรวบรวมแยกต่างหาก สิ่งนี้สามารถเพิ่มความเร็วของกระบวนการสร้างรหัสโดยรวมและช่วยให้มีความยืดหยุ่นกับวิธีการใช้บล็อกในภายหลังกล่าวคือสามารถย้ายตำแหน่งในหน่วยความจำได้ตัวอย่างเช่นการเพิ่ม 1000 ให้กับทุกที่อยู่เพื่อทำการบล็อกบล็อกด้วยเซลล์แอดเดรส 1000 รายการ

ดังนั้นสิ่งที่คอมไพเลอร์เอาท์พุทคือรหัสเครื่องคร่าวๆที่ยังไม่ได้สร้างขึ้นอย่างสมบูรณ์ แต่วางไว้เพื่อให้เรารู้ขนาดของทุกสิ่งในคำอื่น ๆ เพื่อให้เราสามารถเริ่มคำนวณได้ว่าที่อยู่ทั้งหมดจะอยู่ที่ไหน คอมไพเลอร์ยังแสดงรายการสัญลักษณ์ที่เป็นคู่ของชื่อ / ที่อยู่ สัญลักษณ์ที่เกี่ยวข้องกับหน่วยความจำชดเชยในรหัสเครื่องในโมดูลที่มีชื่อ ออฟเซ็ตคือระยะทางที่แน่นอนไปยังตำแหน่งหน่วยความจำของสัญลักษณ์ในโมดูล

นั่นคือสิ่งที่เราไปถึงลิงเกอร์ ตัวเชื่อมโยงจะตบบล็อคของรหัสเครื่องเหล่านี้ด้วยกันตั้งแต่ต้นจนจบและจดไว้ที่จุดเริ่มต้นแต่ละอัน จากนั้นจะคำนวณที่อยู่ที่จะแก้ไขโดยการเพิ่มออฟเซ็ตสัมพัทธ์ภายในโมดูลและตำแหน่งสัมบูรณ์ของโมดูลในโครงร่างใหญ่

เห็นได้ชัดว่าฉันใช้วิธีนี้มากเกินไปเพื่อที่คุณจะได้เข้าใจและฉันก็ไม่ได้ใช้ศัพท์แสงของไฟล์ออบเจ็กต์ตารางสัญลักษณ์ ฯลฯ ซึ่งเป็นส่วนหนึ่งของความสับสน


13

GCC คอมไพล์โปรแกรม C / C ++ ให้สามารถเรียกใช้งานได้ใน 4 ขั้นตอน

ตัวอย่างเช่นgcc -o hello hello.cดำเนินการดังนี้:

1. การประมวลผลล่วงหน้า

การประมวลผลล่วงหน้าผ่าน GNU C Preprocessor ( cpp.exe) ซึ่งมีส่วนหัว ( #include) และขยายมาโคร ( #define)

cpp hello.c > hello.i

ไฟล์กลางผลลัพธ์ "hello.i" มีรหัสแหล่งขยาย

2. การรวบรวม

คอมไพเลอร์รวบรวมคอมไพล์ซอร์สโค้ดที่ถูกประมวลผลล่วงหน้าลงในแอสเซมบลีโค้ดสำหรับตัวประมวลผลเฉพาะ

gcc -S hello.i

ตัวเลือก -S ระบุเพื่อสร้างรหัสการประกอบแทนที่จะเป็นรหัสวัตถุ ไฟล์ชุดประกอบผลลัพธ์คือ "hello.s"

3. การประกอบ

as.exeแอสเซมเบลอร์ ( ) แปลงรหัสแอสเซมบลีเป็นรหัสเครื่องในไฟล์วัตถุ "hello.o"

as -o hello.o hello.s

4. ลิงเกอร์

ในที่สุด linker ( ld.exe) เชื่อมโยงรหัสวัตถุกับรหัสห้องสมุดเพื่อสร้างไฟล์ปฏิบัติการ "hello"

    ld -o hello hello.o ... ห้องสมุด ...

9

ดู URL: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
กระบวนการคอมไพล์ที่สมบูรณ์ของ C ++ นั้นมีการแนะนำอย่างชัดเจนใน URL นี้


2
ขอขอบคุณที่แบ่งปันสิ่งนี้ให้เข้าใจได้ง่ายและตรงไปตรงมา
ทำเครื่องหมาย

ดีทรัพยากรคุณสามารถใส่คำอธิบายพื้นฐานของกระบวนการได้ที่นี่คำตอบนั้นถูกตั้งค่าสถานะโดยอัลกอริธึมที่มีคุณภาพต่ำ b / c สั้นและเป็นแค่ URL
JasonB

บทเรียนสั้น ๆ ที่ดีที่ฉันพบ: calleerlandsson.com/the-four-stages-of-compiling-ac-program
Guy Avraham
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.