Preprocessors พรีโปรเซสเซอร์ ภาษา C
พรีโปรเซสเซอร์ (Preprocessor) ไม่ได้เป็นส่วนหนึ่งของคอมไพเลอร์ แต่เป็นขั้นตอนที่แยกจากกันในการรวบรวม พูดง่ายๆ ว่า C Preprocessor เป็นเพียงเครื่องมือทดแทนข้อความ และสั่งให้คอมไพเลอร์ทำการประมวลผลล่วงหน้าที่จำเป็นก่อนการคอมไพล์จริง เราจะเรียกตัวประมวลผลล่วงหน้า C ว่า CPP
คำสั่งตัวประมวลผลล่วงหน้าทั้งหมดเริ่มต้นด้วยสัญลักษณ์แฮช (#) ต้องเป็นอักขระที่ไม่เว้นว่างตัวแรก และเพื่อให้สามารถอ่านได้ คำสั่งตัวประมวลผลล่วงหน้าควรเริ่มต้นในคอลัมน์แรก ส่วนต่อไปนี้แสดงรายการคำสั่งพรีโปรเซสเซอร์ที่สำคัญทั้งหมด −
Sr.No. | Directive & Description |
---|---|
1 | #define แทนที่ด้วยมาโคร |
2 | #include แทรกส่วนหัวจากไฟล์อื่น |
3 | #undef ยกเลิกการกำหนดมาโคร |
4 | #ifdef ส่งกลับค่าจริงหากมีการกำหนดมาโครนี้ |
5 | #ifndef คืนค่า จริง หากไม่ได้กำหนดมาโครนี้ |
6 | #if ทดสอบว่าเงื่อนไขเวลาคอมไพล์เป็นจริงหรือไม่ |
7 | #else ทางเลือกสำหรับ #if |
8 | #elif# #else และ #if ในประโยคเดียว |
9 | #endif สิ้นสุดเงื่อนไขตัวประมวลผลล่วงหน้า |
10 | #error พิมพ์ข้อความแสดงข้อผิดพลาดบน stderr |
11 | #pragma ออกคำสั่งพิเศษให้กับคอมไพเลอร์โดยใช้วิธีการมาตรฐาน |
Preprocessors Examples (ตัวอย่างพรีโปรเซสเซอร์)
วิเคราะห์ตัวอย่างต่อไปนี้เพื่อทำความเข้าใจคำสั่งต่างๆ
#define MAX_ARRAY_LENGTH 20
คำสั่งนี้บอกให้ CPP แทนที่อินสแตนซ์ของ MAX_ARRAY_LENGTH ด้วย 20 ใช้ #define ใช้สำหรับค่าคงที่เพื่อเพิ่มความสามารถในการอ่าน
#include <stdio.h>
#include "myheader.h"
คำสั่งเหล่านี้บอกให้ CPP รับ stdio.h จาก ไลบรารี (System Libraries) และเพิ่มข้อความลงในไฟล์ต้นฉบับปัจจุบัน บรรทัดถัดไปบอกให้ CPP รับ myheader.h จากไดเร็กทอรีในเครื่องและเพิ่มเนื้อหาลงในไฟล์ต้นฉบับปัจจุบัน
#undef FILE_SIZE
#define FILE_SIZE 42
มันบอกให้ CPP กำหนด FILE_SIZE ที่มีอยู่และกำหนดเป็น 42
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
มันบอกให้ CPP กำหนด MESSAGE ถ้าไม่ได้กำหนด MESSAGE ไว้
#ifdef DEBUG
/* Your debugging statements here */
#endif
มันบอกให้ CPP ประมวลผลข้อความที่แนบมาหากมีการกำหนด DEBUG สิ่งนี้มีประโยชน์หากคุณส่งแฟล็ก -DDEBUG ไปยังคอมไพเลอร์ gcc ในขณะที่คอมไพล์ สิ่งนี้จะกำหนด DEBUG ดังนั้นคุณจึงสามารถเปิดและปิดการดีบักได้ทันทีในระหว่างการคอมไพล์
Predefined Macros (มาโครที่กำหนดไว้ล่วงหน้า)
ANSI C กำหนดมาโครจำนวนหนึ่ง แม้ว่าแต่ละอันจะพร้อมใช้งานในการเขียนโปรแกรม แต่มาโครที่กำหนดไว้ล่วงหน้าไม่ควรแก้ไขโดยตรง
Sr.No. | Macro & Description |
---|---|
1 | __DATE__ วันที่ปัจจุบันเป็นตัวอักษรในรูปแบบ “MMM DD YYYY” |
2 | __TIME__ เวลาปัจจุบันตามตัวอักษรในรูปแบบ “HH:MM:SS” |
3 | __FILE__ มีชื่อไฟล์ปัจจุบันเป็นสตริงตามตัวอักษร |
4 | __LINE__ มีหมายเลขบรรทัดปัจจุบันเป็นค่าคงที่ทศนิยม |
5 | __STDC__ กำหนดเป็น 1 เมื่อคอมไพเลอร์เป็นไปตามมาตรฐาน ANSI |
ลองมาดูตัวอย่างต่อไปนี้กัน −
#include <stdio.h>
int main() {
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );
}
เมื่อโค้ดด้านบนในไฟล์ test.c ถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −
File :D:\Code_C\Macros.c
Date :Nov 18 2021
Time :10:48:21
Line :8
ANSI :1
Preprocessor Operators (ตัวดำเนินการพรีโปรเซสเซอร์)
พรีโปรเซสเซอร์ C มีตัวดำเนินการต่อไปนี้เพื่อช่วยสร้างมาโคร −
Macro Continuation (\) Operator (ตัวดำเนินการต่อเนื่องมาโคร)
โดยปกติมาโครจะถูกจำกัดอยู่ที่บรรทัดเดียว ตัวดำเนินการความต่อเนื่องของมาโคร (\) ใช้เพื่อดำเนินการต่อกับมาโครที่ยาวเกินไปสำหรับบรรทัดเดียว ตัวอย่างเช่น −
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
ตัวดำเนินการ Stringize (#)
ตัวดำเนินการ stringize หรือเครื่องหมายตัวเลข ( ‘#’ ) เมื่อใช้ภายในคำจำกัดความของมาโคร จะแปลงพารามิเตอร์มาโครเป็นค่าคงที่สตริง ตัวดำเนินการนี้อาจใช้ในแมโครที่มีรายการอาร์กิวเมนต์หรือพารามิเตอร์ที่ระบุเท่านั้น ตัวอย่างเช่น −
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −
Carole and Debra: We love you!
ตัวดำเนินการวางโทเค็น (##)
ตัวดำเนินการวางโทเค็น (##) ภายในคำจำกัดความของมาโครรวมสองอาร์กิวเมนต์ อนุญาตให้โทเค็นสองโทเค็นแยกจากกันในคำจำกัดความมาโครเพื่อรวมเป็นโทเค็นเดียว ตัวอย่างเช่น −
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −
token34 = 40
มันเกิดขึ้นเนื่องจากตัวอย่างนี้ส่งผลให้เกิดผลลัพธ์จริงต่อไปนี้จากตัวประมวลผลล่วงหน้า −
printf ("token34 = %d", token34);
ตัวอย่างนี้แสดงให้เห็นว่ากำหนดการ ## โทเค็น n เข้า token34 และที่นี่เราได้ใช้ทั้ง stringize และ token-pasting
ตัวดำเนินการ Defined()
ตัวดำเนินการ พรีโปรเซสเซอร์ defined ใช้ในนิพจน์คงที่เพื่อพิจารณาว่าตัวระบุถูกกำหนดโดยใช้ #define หรือไม่ หากมีการกำหนดตัวระบุที่ระบุ ค่าจะเป็น true (ไม่ใช่ 0 ) หากไม่ได้กำหนดสัญลักษณ์ไว้ ค่าจะเป็นเท็จ (0) ตัวดำเนินการที่กำหนดไว้มีการระบุดังนี้ −
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −
Here is the message: You wish!
มาโครแบบกำหนดพารามิเตอร์
ฟังก์ชันที่มีประสิทธิภาพอย่างหนึ่งของ CPP คือความสามารถในการจำลองฟังก์ชันโดยใช้มาโครแบบกำหนดพารามิเตอร์ ตัวอย่างเช่น เราอาจมีรหัสเพื่อยกกำลังสองตัวเลขดังนี้ −
int square(int x) {
return x * x;
}
เราสามารถเขียนใหม่เหนือโค้ดโดยใช้มาโครดังนี้ −
#define square(x) ((x) * (x))
ต้องกำหนดมาโครที่มีอาร์กิวเมนต์โดยใช้คำสั่ง #define ก่อนจึงจะสามารถใช้ได้ รายการอาร์กิวเมนต์อยู่ในวงเล็บและต้องตามหลังชื่อมาโครทันที ไม่อนุญาตให้มีการเว้นวรรคระหว่างชื่อมาโครและวงเล็บเปิด ตัวอย่างเช่น −
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และรัน มันจะให้ผลลัพธ์ดังต่อไปนี้ −
Max between 20 and 10 is 20