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

Leave a Reply

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