คอมไพเลอร์รู้อย่างไรเกี่ยวกับคลาสอื่นและคุณสมบัติของมัน?


14

ฉันกำลังเขียนภาษาการเขียนโปรแกรมแรกของฉันที่เป็นวัตถุแบบตะวันออกและดีมากด้วยการสร้าง 'คลาส' เดี่ยว แต่ขอบอกว่าผมต้องการให้มีการเรียนการพูดและClassA ClassBให้ทั้งสองไม่มีอะไรเกี่ยวข้องกันแล้วทั้งหมดเป็นสิ่งที่ดี อย่างไรก็ตามการพูดClassAสร้างClassBคำถามนี้ - คำถามที่เกี่ยวข้อง 2 คำถาม:

วิธีจะคอมไพเลอร์รู้เมื่อรวบรวมClassAว่าClassBแม้อยู่และถ้ามันไม่อย่างไรก็รู้ว่าคุณสมบัติของมัน?

ความคิดของฉันในตอนนี้ก็คือ: แทนที่จะรวบรวมแต่ละคลาสในแต่ละครั้ง (เช่นการสแกน, วิเคราะห์คำและสร้างรหัส) แต่ละไฟล์ "(ไม่ใช่ไฟล์จริง ๆ , แต่," คลาส ") ฉันต้องสแกน + แยกแต่ละครั้งก่อน แล้วสร้างรหัสสำหรับทั้งหมดหรือไม่

คำตอบ:


13

ภาษาที่แตกต่างกัน (และคอมไพเลอร์) เข้าหานี้แตกต่างกัน

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

ใน Java คอมไพเลอร์ทราบถึงสิ่งที่อยู่ใน classpath และตรวจสอบวัตถุเหล่านั้นเพื่อเชื่อมโยงกับพวกเขา (ตรวจสอบว่าวิธีการนั้นมีอยู่มีจำนวนอาร์กิวเมนต์ที่เหมาะสม ฯลฯ ... ) Java อาจเชื่อมโยงแบบไดนามิกที่การโหลดรันไทม์ในคลาสอื่น ๆ ที่มันไม่รู้อะไรเลยเมื่อมันถูกคอมไพล์ ดูClass.forNameสำหรับตัวอย่างหนึ่งของการโหลดแบบไดนามิก

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


0

จวนพูดกับ Java, IDE ดูโปรแกรมทั้งหมดในครั้งเดียว; เมื่อมีการอ้างอิง ClassB คอมไพเลอร์ IDE จะดู ทุกอย่างรวมถึงห้องสมุดเป็นหนึ่งเดียวที่สมบูรณ์ เมื่อโปรแกรมพร้อมใช้งานคุณสามารถเปลี่ยน classpaths สลับเข้าและออกไฟล์. class และสลับเวอร์ชันไลบรารีได้ คุณสามารถรวบรวมไฟล์. java แต่ละไฟล์โดยไม่ใช้ IDE (หรือหลีกเลี่ยงการตรวจสอบ) ผลลัพธ์ไม่จำเป็นต้องสอดคล้องกันเลยและหากไม่ใช่คุณจะได้รับข้อยกเว้นแบบรันไทม์ (หนึ่งในหลาย ๆ สิ่งที่ IDE พยายามทำเพื่อคุณคือการเปลี่ยนข้อผิดพลาดรันไทม์ให้เป็นเวลาคอมไพล์หรือมากกว่าการแก้ไขเวลาข้อผิดพลาด)

C # นั้นเหมือนกันและฉันไม่คิดว่า C และ C ++ นั้นแตกต่างกันมากในสิ่งที่ Java และ C # IDE กำลังทำอยู่ก็คือการสร้างส่วนหัวสไตล์ C / C ++ สำหรับคุณที่อยู่เบื้องหลัง


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

@ DonalFellows: ปัญหาของฉันหายไปจากไลบรารี ("แต่ฉันใส่มันลงในเครื่องทั้งหมดของฉัน!") และคอมไพล์โปรแกรมที่กำลังรันใหม่ (โดยไม่ได้ตั้งใจแลกเปลี่ยนไฟล์. class ฉันสามารถคาดการณ์การอัปเดตแพ็คเกจที่ไม่ตรงกับโปรแกรมหลักอีกต่อไปแม้ว่าฉันจะยังไม่ได้ทำก็ตาม ฉันไม่ได้ทำมากว่าด้วย C และ .dll ของ (และมีมันทำเพื่อฉัน) ปีที่ผ่านมา ฉันเชื่อและหวังว่าจะมีการปกป้องอีกมากในตอนนี้ที่ไม่ได้อยู่ที่นั่น
RalphChapin

ฉันไม่เข้าใจว่า IDE ทำอะไรกับคอมไพเลอร์ / ลิงเกอร์รู้วิธีแก้ปัญหาคอมไพล์ / ลิงเกอร์ แก้ไขให้ถูกต้องถ้าฉันผิด แต่ IDE นั้นเป็นฉากที่เกี่ยวข้องกับปัญหาทั้งหมด (ยกเว้นว่ามันทำให้งานในโปรแกรมเมอร์ง่ายขึ้น) ทำไม? เพราะในทางทฤษฎีแล้ว IDE ใช้คอมไพเลอร์ในพื้นหลัง
Thomas Eding

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

0

บางครั้งภาษาที่เก่ากว่านั้นเข้มงวดมากขึ้น พิจารณาสิ่งที่เป็นไปได้ใน Java:

public interface Ifc {
    public static final Ifc MY_CONSTANT = new Implem();
}

public class Implem implements Ifc {
}

ฉันเคยเห็นรูปแบบการต่อต้านด้านบนแล้วและมันก็น่าเกลียดจริงๆ (ฉันจะห้ามมัน) หน่วยการรวบรวมทั้งสองใช้ซึ่งกันและกัน แต่ Ifc สามารถคอมไพล์โค้ดได้โดยไม่ต้องคอมไพล์ Implem รหัสรวบรวม .class, เทียบเท่ากับ .obj C, มี "ข้อมูลการเชื่อมโยง" นำเข้าของ Implem Implem()เรียกนวกรรมิก ชั้น Implem นั้นสามารถรวบรวมได้โดยไม่มีปัญหา ส่วนหนึ่งของ ClassLoader - ทำข้อมูลเริ่มต้น / สร้าง JVM คลาสและบางส่วนของ Java Virtual Machine เองเล่นเป็นตัวเชื่อมโยงการรวมทั้งหมด

ตัวอย่างเช่นการคอมไพล์ด้วยไลบรารีหนึ่งเวอร์ชันและการรันด้วยไลบรารีเวอร์ชันอื่นจะรับรู้ข้อผิดพลาดรันไทม์

ดังนั้นคำตอบ:การคอมไพล์จะส่งมอบหน่วยของโค้ดออบเจ็กต์ที่คอมไพล์ซึ่งต้องดูเป็นรหัส + ข้อมูล + API สำหรับการเชื่อมโยงเข้าด้วยกัน

คอมไพเลอร์ควรหลังจากนั้นยังทำบรรจุด้วยกันและตรวจสอบการเชื่อมโยงของ API; ระยะที่สอง

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.