Compilation Process in C


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

กระบวนการคอมไพล์ในภาษา C ประกอบด้วยสี่ขั้นตอน: 1.การประมวลผลล่วงหน้า 2. การคอมไพล์ 3.การประกอบ 4.การเชื่อมโยง จากนั้นเราเรียกใช้ไฟล์ปฏิบัติการที่ได้รับเพื่อให้ได้ผลลัพธ์บนหน้าจอ


Compilation คืออะไร?


ก่อนดำดิ่งสู่ความหมายดั้งเดิมของ Compilation ลองพิจารณาตัวอย่างที่มีคนกพูดภาษาฮินดีและ คนกต้องการคุยกับขที่รู้แค่ภาษาอังกฤษดังนั้นตอนนี้คนใดคนหนึ่งจึงต้องการล่ามเพื่อแปล คำที่จะสื่อสารกัน กระบวนการนี้เรียกว่าการแปลหรือในแง่ของการเขียนโปรแกรมเรียกว่ากระบวนการ Compilation

กระบวนการคอมไพล์ในภาษา C กำลังแปลงรหัสของมนุษย์ที่เข้าใจได้ให้เป็นรหัสที่เข้าใจได้ของคอมพิวเตอร์ และตรวจสอบไวยากรณ์และความหมายของโค้ดเพื่อระบุข้อผิดพลาดทางไวยากรณ์หรือคำเตือนใดๆ ที่มีอยู่ในโปรแกรม C ของเรา สมมติว่าเราต้องการรันโปรแกรม C ของเราที่เขียนใน IDE (Integrated Development Environment) ในกรณีนั้น จะต้องผ่านขั้นตอนต่างๆ ของ Compilation (การแปล) เพื่อให้กลายเป็นไฟล์ปฏิบัติการที่คอมพิวเตอร์สามารถเข้าใจได้

Compilation ในภาษา C ประกอบด้วย 4 ขั้นตอนคือ


  1. Preprocessing (การประมวลผลล่วงหน้า)
  2. Compiling (กำลังรวบรวม)
  3. Assembling (การประกอบ)
  4. Linking (การเชื่อมโยง)

The Compilation Process in C


a. Pre-Processing

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

i. Comments Removal

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

/* This is a 
 multi-line comment in C */

#include<stdio.h>

int main()
{
    // this is a single-line comment in C
    
    return 0;
}


ii. Macros Expansion

Macros มาโครคือค่าคงที่หรือนิพจน์ที่กำหนดโดยใช้ คำสั่ง #defineในภาษา C การเรียกมาโครนำไปสู่การขยายมาโคร ตัวประมวลผลล่วงหน้าจะสร้างไฟล์ระดับกลางโดยที่คำสั่งระดับการประกอบที่เขียนไว้ล่วงหน้าบางส่วนจะแทนที่นิพจน์หรือค่าคงที่ที่กำหนดไว้ (โดยทั่วไปแล้วจะเป็นการจับคู่โทเค็น) เพื่อแยกความแตกต่างระหว่างคำสั่งดั้งเดิมและคำสั่งการประกอบที่เกิดจากการขยายมาโคร เครื่องหมาย’+’จะถูกเพิ่มลงในคำสั่งที่ขยายมาโครทุกอัน

ตัวอย่างมาโคร:

การกำหนดค่า

#define G 9.8

การกำหนดนิพจน์

#define SUM(a,b) (a + b)


iii. File inclusion

การรวมไฟล์ในภาษา C เป็นการเพิ่มไฟล์ อื่น ที่มีโค้ดที่เขียนไว้ล่วงหน้าบางส่วนในโปรแกรม C ของเราในระหว่างการประมวลผลล่วงหน้า ทำได้โดยใช้คำสั่ง #include การรวมไฟล์ระหว่างการประมวลผลล่วงหน้าทำให้เนื้อหาทั้งหมดของชื่อไฟล์ถูกเพิ่มลงในซอร์สโค้ด แทนที่ คำสั่ง #include เพื่อสร้างไฟล์ระดับกลางใหม่

ตัวอย่าง: หากเราต้องใช้ฟังก์ชัน อินพุต/เอาต์พุตพื้นฐาน เช่น printf() และ scanf() ในโปรแกรม C ของเรา เราต้องรวมไฟล์ส่วนหัวเอาต์พุตอินพุตมาตรฐาน ที่กำหนดไว้ ล่วงหน้าเช่น stdio.h

#include <stdio.h>


iv. Conditional Compilation

การคอมไพล์แบบมีเงื่อนไขกำลังทำงานหรือหลีกเลี่ยงบล็อกของโค้ดหลังจากตรวจสอบว่ามีการ กำหนด มาโครหรือไม่ (ค่าคงที่หรือนิพจน์ที่กำหนดโดยใช้ #define ) ตัวประมวลผลล่วงหน้าจะแทนที่คำสั่งการคอมไพล์แบบมีเงื่อนไขทั้งหมดด้วยโค้ดแอสเซมบลีที่กำหนดไว้ล่วงหน้าบางส่วน และส่งไฟล์ที่ขยายใหม่ไปยังคอมไพเลอร์ การคอมไพล์แบบมีเงื่อนไขสามารถทำได้โดยใช้คำสั่งเช่น #ifdef , #endif , #ifndef , #if , #else และ #elif ในโปรแกรม C ตัวอย่าง :

  • การพิมพ์ มาโคร AGEหาก กำหนดมาโคร AGE ไว้ มิฉะนั้นจะพิมพ์ Not Definedและสิ้นสุดบล็อกการคอมไพล์แบบมีเงื่อนไขด้วยคำสั่ง #endif
#include <stdio.h>

// if we uncomment the below line, then the program will print AGE in the output.
// #define AGE 18

int main()
{
	// if `AGE` is defined then print the `AGE` else print "Not Defined"
	#ifdef AGE
		printf("Age is %d", AGE);
	#else
		printf("Not Defined");
	#endif

	return 0;
}

คำอธิบาย:

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

ตอนนี้เรามาดูรูปภาพด้านล่างที่แสดงให้เห็นว่าพรีโปรเซสเซอร์แปลงไฟล์ซอร์สโค้ดของเราเป็นไฟล์ระดับกลางได้อย่างไร ไฟล์ระดับกลางมีนามสกุล.i และเป็นรูปแบบขยายของโปรแกรม C ของเราที่มีเนื้อหาทั้งหมดของไฟล์ส่วนหัว การขยายมาโคร และการคอมไพล์ตามเงื่อนไข

b. Compiling

ขั้นตอนการคอมไพล์ในภาษา C ใช้ซอฟต์แวร์คอมไพเลอร์ ในตัว เพื่อแปลงไฟล์ระดับกลาง ( .i ) เป็นไฟล์ แอสเซมบลี ( .s ) ที่มีคำแนะนำระดับแอสเซมบลี (รหัสระดับต่ำ) เพื่อเพิ่มประสิทธิภาพการทำงานของคอมไพเลอร์โปรแกรมแปลไฟล์ระดับกลางเพื่อสร้างไฟล์แอสเซมบลี

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

ภาพด้านล่างแสดงตัวอย่างการทำงานของขั้นตอนการคอมไพล์

c. Assembling

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

ไฟล์ที่สร้างขึ้นมีชื่อเดียวกับไฟล์แอสเซมบลีและเรียกว่าไฟล์อ็อบเจ็กต์ที่มีนามสกุล .obj ใน DOS และ .o ใน UNIX OS

ภาพด้านล่างแสดงตัวอย่างการทำงานของขั้นตอนการประกอบ ไฟล์แอสเซมบลี area.s ถูกแปลเป็นอ็อบเจ็กต์ไฟล์ area.o ที่มีชื่อเดียวกัน แต่มีนามสกุลต่างกัน

ตัวอย่าง
โปรแกรม C เพื่อแสดง Hello World! บนหน้าจอเอาท์พุท

// Simple Hello World program in C
#include<stdio.h>

int main()
{
    // printf() is a output function which prints
    // the passed string in the output console
    printf("Hello World!");
    
    return 0;
}

บันทึก:

Hello World เล็ก ๆ นี้ ! 
โปรแกรมต้องผ่านหลายขั้นตอนของกระบวนการคอมไพล์เพื่อให้ได้ผลลัพธ์บนหน้าจอ

คำอธิบาย:

  • ในการคอมไพล์โค้ดข้างต้น ให้ใช้คำสั่งนี้ในเทอร์มินัล : gcc hello.c -o hello
  • ขั้นแรก การประมวลผลล่วงหน้าของโปรแกรม C ของเราเริ่มต้นขึ้น ความคิดเห็นจะถูกลบออกจากโปรแกรม เนื่องจากไม่มีคำสั่งมาโครในโปรแกรมนี้ ดังนั้นการขยายมาโครจะไม่เกิดขึ้น นอกจากนี้ เราได้รวม ไฟล์ส่วนหัว stdio.h และระหว่างช่วงก่อน การประมวลผล การประกาศฟังก์ชันอินพุต/เอาต์พุตมาตรฐาน เช่น printf() , scanf() เป็นต้น ถูกเพิ่มในโปรแกรม C ของเรา ในระหว่างขั้นตอนการคอมไพล์โปรแกรมของเรา คำสั่งทั้งหมดจะถูกแปลงเป็นคำสั่งระดับแอสเซมบลีโดยใช้ซอฟต์แวร์คอมไพเลอร์
  • คำแนะนำระดับการประกอบสำหรับโปรแกรมด้านบน ( ไฟล์ hello.s ):
	.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 11, 0	sdk_version 12, 1
	.globl	_main                           ## -- Begin function main
	.p2align	4, 0x90
_main:                                  ## @main
	.cfi_startproc
## %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	subq	$16, %rsp
	movl	$0, -4(%rbp)
	leaq	L_.str(%rip), %rdi
	movb	$0, %al
	callq	_printf
	xorl	%eax, %eax
	addq	$16, %rsp
	popq	%rbp
	retq
	.cfi_endproc
                                        ## -- End function
	.section	__TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
	.asciz	"Hello World!"

.subsections_via_symbols


  • คุณสามารถรับ ไฟล์ hello.s ด้านบนได้ โดยใช้คำสั่ง: gcc -S hello.c ในเทอร์มินัล


  • ไฟล์ hello.s ถูกแปลงเป็นรหัสไบนารีโดยใช้โปรแกรมแอสเซมเบลอร์และสร้างไฟล์อ็อบเจ็กต์ hello.obj ใน DOS และ hello.o ใน UNIX OS
  • ตอนนี้ ตัวเชื่อมโยงเพิ่มคำจำกัดความที่จำเป็นลงในไฟล์อ็อบเจ็กต์โดยใช้ไฟล์ไลบรารี และสร้างไฟล์เรียกทำงาน hello.exe ใน DOS และ hello.out ใน UNIX OS
  • เมื่อเราเรียกใช้ hello.exe / hello.out เราจะได้รับ Hello World! เอาต์พุตบนหน้าจอ

Flow Diagram of the Program


ให้เราดูแผนภาพการไหลของโปรแกรมในกระบวนการคอมไพล์ใน C :

  • เรามีไฟล์โปรแกรม C ที่มีนามสกุลเป็น .c เช่นไฟล์ hello.c
  • ขั้นตอนที่ 1 คือการประมวลผลล่วงหน้าของไฟล์ส่วนหัวคำสั่งทั้งหมดที่ขึ้นต้นด้วย # (สัญลักษณ์แฮช) และความคิดเห็นจะถูกแทนที่/ลบออกระหว่างการประมวลผลล่วงหน้าด้วยความช่วยเหลือของตัวประมวลผลล่วงหน้า มันสร้างไฟล์ระดับกลางที่มี นามสกุลไฟล์ .i เช่นไฟล์ hello.i
  • ขั้นตอนที่ 2 คือการรวบรวมไฟล์ hello.i ซอฟต์แวร์คอมไพเลอร์แปลไฟล์ hello.i เป็น hello.s พร้อมคำแนะนำระดับการประกอบ (รหัสระดับต่ำ)
  • ขั้นตอนที่ 3 คำแนะนำเกี่ยวกับรหัสระดับชุดประกอบจะถูกแปลงเป็นรหัสที่เครื่องเข้าใจได้ (รูปแบบไบนารี/เลขฐานสิบหก) โดยแอสเซมเบลอร์ ไฟล์ที่สร้างขึ้นเรียกว่าไฟล์อ็อบเจ็กต์ที่มีนามสกุล .obj / .o เช่นไฟล์ hello.obj / hello.o
  • ขั้นตอนที่ 4 , Linkerใช้เพื่อเชื่อมโยงไฟล์ไลบรารีกับไฟล์อ็อบเจ็กต์เพื่อกำหนดคำสั่งที่ไม่รู้จัก มันสร้างไฟล์ปฏิบัติการที่มีนามสกุล .exe / .out เช่นไฟล์ hello.exe / hello.out
  • ต่อไป เราสามารถเรียกใช้ไฟล์เรียกทำงาน hello.exe / hello.out เพื่อรับเอาต์พุตที่ต้องการบนหน้าต่างเอาต์พุตของเรา นั่นคือ Hello World! .


บทสรุป

  • กระบวนการคอมไพล์ในภาษา C เรียกอีกอย่างว่ากระบวนการแปลงรหัสที่เข้าใจได้ของมนุษย์ ( โปรแกรม C ) เป็นรหัสที่เข้าใจได้ของเครื่อง ( Binary Code ))
  • กระบวนการคอมไพล์ในภาษาซีประกอบด้วยสี่ขั้นตอน: การประมวลผลล่วงหน้า การคอมไพล์ การประกอบ และการลิงก์
  • เครื่องมือตัวประมวลผลล่วงหน้าช่วยในการลบความคิดเห็น การขยายมาโคร การรวมไฟล์ และการรวบรวมตามเงื่อนไข คำสั่งเหล่านี้ดำเนินการในขั้นตอนแรกของกระบวนการคอมไพล์ ซอฟต์แวร์คอมไพเลอร์ช่วยเพิ่มประสิทธิภาพการทำงานของโปรแกรมและแปลไฟล์ระดับกลางเป็นไฟล์แอสเซมบลี
  • Assembler ช่วยแปลงไฟล์แอสเซมบลีเป็นไฟล์อ็อบเจ็กต์ที่มีรหัสระดับเครื่อง
  • Linker ใช้สำหรับเชื่อมโยงไฟล์ไลบรารีกับไฟล์อ็อบเจ็กต์ เป็นขั้นตอนสุดท้ายในการรวบรวมเพื่อสร้างไฟล์ปฏิบัติการ


credit : https://www.scaler.com/topics/c/

Leave a Reply

Your email address will not be published. Required fields are marked *