C พรีโปรเซสเซอร์

จากวิกิพีเดีย สารานุกรมเสรี
ข้ามไปที่การนำทาง ข้ามไปที่การค้นหา

C preprocessorเป็นpreprocessor แมโครสำหรับC , Objective-CและC ++คอมพิวเตอร์ภาษาโปรแกรม พรีโพรเซสเซอร์ให้ความสามารถในการรวมของไฟล์ส่วนหัว , แมโครขยายเงื่อนไขการรวบรวมและการควบคุมสาย

ในการใช้งาน C จำนวนมากก็คือแยกโปรแกรมเรียกโดยคอมไพเลอร์เป็นส่วนแรกของการแปล

ภาษาของ preprocessor สั่งเป็นเพียงนิดหน่อยที่เกี่ยวข้องกับไวยากรณ์ของ C และอื่น ๆ บางครั้งใช้ในการประมวลผลชนิดอื่น ๆ ของไฟล์ข้อความ [1]

ประวัติ

Preprocessor ได้รับการแนะนำให้รู้จักกับ C รอบ 1,973 แนะนำของอลันไนเดอร์และยังอยู่ในการรับรู้ของประโยชน์ของกลไกการรวมไฟล์ที่มีอยู่ในBCPLและPL / I เวอร์ชันดั้งเดิมอนุญาตให้รวมไฟล์และทำการแทนที่สตริงอย่างง่ายเท่านั้น: #includeและ#defineมาโครแบบไม่มีพารามิเตอร์ หลังจากนั้นไม่นาน ส่วนขยายนี้ขยายโดยMike Leskส่วนใหญ่และ John Reiser เพื่อรวมมาโครเข้ากับอาร์กิวเมนต์และการรวบรวมตามเงื่อนไข [2]

ตัวประมวลผลล่วงหน้า C เป็นส่วนหนึ่งของประเพณีภาษามหภาคที่ยาวนานที่ Bell Labs ซึ่งเริ่มต้นโดย Douglas Eastwood และDouglas McIlroyในปี 1959 [3]

เฟส

การประมวลผลล่วงหน้าถูกกำหนดโดยสี่ขั้นตอนแรก (จากแปด) ของการแปลที่ระบุในมาตรฐาน C

  1. การแทนที่ Trigraph: ตัวประมวลผลล่วงหน้าจะแทนที่ลำดับไตรกราฟด้วยอักขระที่แสดง
  2. การต่อบรรทัด: เส้นที่มาทางกายภาพที่ต่อเนื่องด้วยลำดับการขึ้นบรรทัดใหม่แบบEscape จะถูกต่อเข้าด้วยกันเพื่อสร้างเส้นตรรกะ
  3. tokenization การ preprocessor แบ่งผลลงในราชสกุล preprocessingและช่องว่าง มันแทนที่ความคิดเห็นด้วยช่องว่าง
  4. การขยายมาโครและการจัดการคำสั่ง: การประมวลผลบรรทัดคำสั่งล่วงหน้า รวมถึงการรวมไฟล์และการคอมไพล์ตามเงื่อนไข ตัวประมวลผลล่วงหน้าขยายมาโครพร้อมกัน และตั้งแต่รุ่น C มาตรฐานปี 1999 จะจัดการ_Pragmaโอเปอเรเตอร์

รวมไฟล์

การใช้งานตัวประมวลผลล่วงหน้าที่พบบ่อยที่สุดอย่างหนึ่งคือการรวมไฟล์อื่น:

#include <stdio.h> 

int หลัก( เป็นโมฆะ) 
{
    printf ( "สวัสดีชาวโลก! \n " );
    กลับ0 ; 
}

ตัวประมวลผลล่วงหน้าแทนที่บรรทัด#include <stdio.h>ด้วยเนื้อหาที่เป็นข้อความของไฟล์ 'stdio.h' ซึ่งประกาศprintf() ฟังก์ชันเหนือสิ่งอื่นใด

#include "stdio.h"นอกจากนี้ยังสามารถเขียนโดยใช้คำพูดสองเช่น ถ้าชื่อไฟล์อยู่ภายในวงเล็บเหลี่ยม ไฟล์จะถูกค้นหาในคอมไพเลอร์มาตรฐานรวมถึงพาธ หากชื่อไฟล์อยู่ภายในเครื่องหมายคำพูดคู่ เส้นทางการค้นหาจะถูกขยายเพื่อรวมไดเร็กทอรีไฟล์ต้นทางปัจจุบัน คอมไพเลอร์ C และสภาพแวดล้อมการเขียนโปรแกรมทั้งหมดมีสิ่งอำนวยความสะดวกที่ช่วยให้โปรแกรมเมอร์กำหนดตำแหน่งที่จะพบไฟล์ที่รวมอยู่ สิ่งนี้สามารถนำมาใช้ผ่านแฟล็กบรรทัดคำสั่ง ซึ่งสามารถกำหนดพารามิเตอร์ได้โดยใช้makefileเพื่อให้สามารถสลับชุดไฟล์รวมที่แตกต่างกันสำหรับระบบปฏิบัติการที่แตกต่างกันได้ เป็นต้น

ตามธรรมเนียมแล้ว การรวมไฟล์จะถูกตั้งชื่อด้วยนามสกุล.hหรือ. hpp อย่างไรก็ตาม ไม่มีข้อกำหนดว่าจะต้องปฏิบัติตาม ไฟล์ที่มีนามสกุล.defอาจหมายถึงไฟล์ที่ออกแบบมาให้รวมหลายครั้ง โดยแต่ละครั้งจะขยายเนื้อหาที่ซ้ำกัน #include "icon.xbm"มีแนวโน้มที่จะอ้างถึงไฟล์อิมเมจXBM (ซึ่งในเวลาเดียวกันกับไฟล์ต้นฉบับ C)

#includeมักจะบังคับให้ใช้#includeยามหรือ#pragma onceเพื่อป้องกันการรวมสองครั้ง

การรวบรวมแบบมีเงื่อนไข

ถ้า-อื่นสั่ง#if, #ifdef, #ifndef, #else, #elifและ#endifสามารถใช้สำหรับการรวบรวมเงื่อนไข #ifdefและ#ifndefเป็นชวเลขง่าย ๆ สำหรับ#if defined(...)และ#if !defined(...).

#if VERBOSE >= 2 
printf ( "ติดตามข้อความ" );  
#endif

คอมไพเลอร์ส่วนใหญ่กำหนดเป้าหมายMicrosoft Windows_WIN32โดยปริยายกำหนด [4]ซึ่งช่วยให้โค้ด รวมถึงคำสั่งตัวประมวลผลล่วงหน้า คอมไพล์ได้เฉพาะเมื่อกำหนดเป้าหมายระบบ Windows คอมไพเลอร์บางตัวกำหนดWIN32แทน สำหรับคอมไพเลอร์ที่ไม่ได้กำหนด_WIN32มาโครโดยปริยายสามารถระบุได้ในบรรทัดรับคำสั่งของคอมไพเลอร์ โดยใช้-D_WIN32.

#ifdef __unix__ /* __unix__ มักจะถูกกำหนดโดยคอมไพเลอร์ที่กำหนดเป้าหมายไปยังระบบ Unix */
# รวม<unistd.h>  
#elif กำหนด _WIN32 /* _WIN32 ถูกกำหนดโดยคอมไพเลอร์ที่กำหนดเป้าหมายไปยังระบบ Windows 32 หรือ 64 บิต */
# รวม<windows.h>  
#endif

โค้ดตัวอย่างจะทดสอบว่าแมโคร__unix__กำหนดไว้หรือไม่ ถ้าใช่ ไฟล์<unistd.h>จะถูกรวมไว้ด้วย มิฉะนั้น จะทดสอบว่า_WIN32กำหนดมาโครแทนหรือไม่ ถ้าใช่ ไฟล์<windows.h>จะถูกรวมไว้ด้วย

#ifตัวอย่างที่ซับซ้อนกว่านี้สามารถใช้ตัวดำเนินการได้ เช่น

#if !(กำหนด __LP64__ || กำหนด __LLP64__) || กำหนด _WIN32 && !defined _WIN64 
// เรากำลังรวบรวมสำหรับระบบ 32 บิต#else // เรากำลังรวบรวมสำหรับระบบ 64 บิต#endif	

	

การแปลอาจทำให้ล้มเหลวได้โดยใช้#errorคำสั่ง:

#if RUBY_VERSION == 190 
# ข้อผิดพลาด 1.9.0 ไม่รองรับ
#endif

นิยามมาโครและการขยาย

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

#define <identifier> <replacement token list>                     // มาโครเหมือนวัตถุ
#define <identifier> (<รายการพารามิเตอร์>) <replacement token list>   // มาโครที่เหมือนฟังก์ชัน พารามิเตอร์หมายเหตุ

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

คำจำกัดความของมาโครสามารถลบได้ด้วย#undef:

#undef <identifier>                                               // ลบแมโคร

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

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

#define PI 3.14159

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

ตัวอย่างของมาโครที่เหมือนฟังก์ชันคือ:

#define RADTODEG(x) ((x) * 57.29578)

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

วงเล็บที่สองอยู่ในวงเล็บคู่ของมันเอง เพื่อหลีกเลี่ยงความเป็นไปได้ของลำดับการดำเนินการที่ไม่ถูกต้องเมื่อเป็นนิพจน์แทนที่จะเป็นค่าเดียว ตัวอย่างเช่น นิพจน์ขยายอย่างถูกต้องเป็น; ไม่มีวงเล็บให้มีความสำคัญเหนือการคูณ xRADTODEG(r + 1)((r + 1) * 57.29578)(r + 1 * 57.29578)

ในทำนองเดียวกัน วงเล็บคู่นอกจะรักษาลำดับการทำงานที่ถูกต้อง ตัวอย่างเช่นขยายเป็น; ไม่มีวงเล็บให้มีความสำคัญกับการหาร 1 / RADTODEG(r)1 / ((r) * 57.29578)1 / (r) * 57.29578

ลำดับการขยาย

การขยายมาโครที่เหมือนฟังก์ชันเกิดขึ้นในขั้นตอนต่อไปนี้:

  1. การดำเนินการสตริงจะถูกแทนที่ด้วยการแสดงข้อความของรายการแทนที่อาร์กิวเมนต์ (โดยไม่ต้องทำการขยาย)
  2. พารามิเตอร์จะถูกแทนที่ด้วยรายการแทนที่ (โดยไม่ต้องทำการขยาย)
  3. การดำเนินการต่อกันจะถูกแทนที่ด้วยผลลัพธ์ที่ต่อกันของตัวถูกดำเนินการสองตัว (โดยไม่ขยายโทเค็นที่เป็นผลลัพธ์)
  4. โทเค็นที่มาจากพารามิเตอร์จะถูกขยาย
  5. โทเค็นที่ได้จะถูกขยายตามปกติ

สิ่งนี้อาจให้ผลลัพธ์ที่น่าประหลาดใจ:

#define HE HI 
#define LLO _THERE 
#define HELLO "HI THERE" 
#define CAT(a,b) a##b 
#define XCAT(a,b) CAT(a,b) 
#define CALL(fn) fn(เขา, LLO) 
CAT ( HE , LLO ) // "HI THERE" เนื่องจากการต่อกันเกิดขึ้นก่อนการขยายXCAT ปกติ( HE , LLO ) // HI_THERE เนื่องจากโทเค็นที่เกิดจากพารามิเตอร์ ("HE" และ "LLO") จะถูกขยายก่อนCALL ( CAT ) // "HI THERE" เนื่องจากพารามิเตอร์ถูกขยายก่อน  
  
 

แมโครและคำสั่งพิเศษ

สัญลักษณ์บางอย่างจำเป็นต้องกำหนดโดยการใช้งานระหว่างการประมวลผลล่วงหน้า ซึ่งรวมถึง__FILE__และ__LINE__ซึ่งกำหนดไว้ล่วงหน้าโดยตัวประมวลผลล่วงหน้า ซึ่งขยายเป็นไฟล์ปัจจุบันและหมายเลขบรรทัด ตัวอย่างเช่นต่อไปนี้:

// การดีบักมาโครเพื่อให้เราสามารถตรึงที่มาของข้อความได้อย่างรวดเร็ว
// ไม่ดี
#define WHERESTR "[ไฟล์ %s, บรรทัด %d]: " 
#define WHEREARG __FILE__, __LINE__ 
#define DEBUGPRINT2(...) fprintf(stderr) , __VA_ARGS__) 
#define DEBUGPRINT(_fmt, ...) DEBUGPRINT2(WHERESTR _fmt, WHEREARG, __VA_ARGS__) 
// OR 
// is good 
#define DEBUGPRINT(_fmt, ...) fprintf(stderr, "[file %s, line %d]: " _fmt, __FILE__, __LINE__, __VA_ARGS__)

  DEBUGPRINT ( "เฮ้ x=%d \n " , x ); 

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

#line 314 "pi.c" 
printf ( "line=%d file=%s \n " , __LINE__ , __FILE__ );  

สร้างฟังก์ชัน printf:

printf ( "line=%d file=%s \n " , 314 , "pi.c" );  

รหัสที่มาแก้จุดบกพร่องดูยังตำแหน่งแหล่งที่กำหนดด้วยและ__FILE__ __LINE__ซึ่งช่วยให้สามารถแก้จุดบกพร่องของซอร์สโค้ดเมื่อใช้ C เป็นภาษาเป้าหมายของคอมไพเลอร์ สำหรับภาษาอื่นโดยสิ้นเชิง มาตรฐาน Cแรกระบุว่ามาโคร__STDC__ถูกกำหนดเป็น 1 หากการใช้งานเป็นไปตามมาตรฐาน ISO และ 0 มิฉะนั้น และมาโครที่__STDC_VERSION__กำหนดเป็นตัวอักษรตัวเลขที่ระบุเวอร์ชันของมาตรฐานที่ได้รับการสนับสนุนโดยการใช้งาน คอมไพเลอร์ C++ มาตรฐานรองรับ__cplusplusมาโคร คอมไพเลอร์ที่ทำงานในโหมดที่ไม่ได้มาตรฐานต้องไม่ตั้งค่ามาโครเหล่านี้หรือต้องกำหนดอย่างอื่นเพื่อส่งสัญญาณถึงความแตกต่าง

แมโครมาตรฐานอื่นๆ ได้แก่__DATE__วันที่ปัจจุบัน และ__TIME__เวลาปัจจุบัน

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

แมโครที่สามารถใช้เป็นจำนวนที่แตกต่างของการขัดแย้ง ( แมโคร variadic ) จะไม่ได้รับอนุญาตใน C89 แต่ถูกนำมาใช้โดยจำนวนของคอมไพเลอร์และมาตรฐานในC99 แมโคร Variadic มีประโยชน์อย่างยิ่งเมื่อเขียน wrapper ไปยังฟังก์ชันโดยใช้พารามิเตอร์จำนวนตัวแปรprintfเช่น เมื่อบันทึกคำเตือนและข้อผิดพลาด

หนึ่งในรูปแบบการใช้งานเล็ก ๆ น้อย ๆ ที่รู้จักของ preprocessor C เป็นที่รู้จักกันX-แมโคร [5] [6] [7] X-มาโครเป็นไฟล์ส่วนหัวโดยทั่วไปจะใช้นามสกุล ".def" แทน ".h" ดั้งเดิม ไฟล์นี้มีรายการการเรียกมาโครที่คล้ายกัน ซึ่งสามารถเรียกว่า "มาโครคอมโพเนนต์" ไฟล์รวมจะถูกอ้างอิงซ้ำแล้วซ้ำอีก

คอมไพเลอร์จำนวนมากกำหนดมาโครเพิ่มเติมที่ไม่ได้มาตรฐาน แม้ว่าสิ่งเหล่านี้มักจะได้รับการจัดทำเป็นเอกสารไม่ดีก็ตาม การอ้างอิงทั่วไปสำหรับมาโครเหล่านี้คือโปรเจ็กต์มาโครคอมไพเลอร์ C/C++ ที่กำหนดไว้ล่วงหน้าซึ่งแสดงรายการ "มาโครคอมไพเลอร์ที่กำหนดไว้ล่วงหน้าต่างๆ ที่สามารถใช้เพื่อระบุมาตรฐาน คอมไพเลอร์ ระบบปฏิบัติการ สถาปัตยกรรมฮาร์ดแวร์ และแม้แต่ไลบรารีรันไทม์พื้นฐาน ในเวลารวบรวม".

การทำให้เป็นสตริงของโทเค็น

ตัวดำเนินการ # (เรียกว่า "ตัวดำเนินการสตริง") แปลงโทเค็นเป็นตัวอักษรสตริง C โดยหลีกเลี่ยงเครื่องหมายคำพูดหรือแบ็กสแลชอย่างเหมาะสม

ตัวอย่าง:

#define str(s) #s

str ( p = "foo \n " ;) // เอาต์พุต "p = \"foo\\n\";" str ( \ n ) // เอาต์พุต "\n"   
           

หากคุณต้องการทำให้การขยายตัวของอาร์กิวเมนต์แมโครมีความเข้มงวด คุณต้องใช้มาโครสองระดับ:

#define xstr(s) str(s) 
#define str(s) #s 
#define foo 4

str ( foo ) // เอาต์พุต "foo" xstr ( foo ) // เอาต์พุต "4"   
  

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

การรวมโทเค็น

ตัวดำเนินการ ## (เรียกว่า "ตัวดำเนินการวางโทเค็น") เชื่อมสองโทเค็นเป็นโทเค็นเดียว

ตัวอย่าง:

#define DECLARE_STRUCT_TYPE(ชื่อ) typedef ชื่อโครงสร้าง##_s ชื่อ##_t

DECLARE_STRUCT_TYPE ( g_object ); // ผลลัพธ์: typedef struct g_object_s g_object_t; 

ข้อผิดพลาดในการรวบรวมที่ผู้ใช้กำหนด

#errorสั่งแสดงข้อความผ่านกระแสข้อผิดพลาด

#error "ข้อความแสดงข้อผิดพลาด"

การใช้งาน

การใช้งาน C, C++ และ Objective-C ทั้งหมดมีตัวประมวลผลล่วงหน้า เนื่องจากการประมวลผลล่วงหน้าเป็นขั้นตอนที่จำเป็นสำหรับภาษาเหล่านั้น และลักษณะการทำงานได้รับการอธิบายโดยมาตรฐานอย่างเป็นทางการสำหรับภาษาเหล่านี้ เช่น มาตรฐาน ISO C

การดำเนินการอาจให้ส่วนขยายและการเบี่ยงเบนของตนเอง และแตกต่างกันไปตามระดับของการปฏิบัติตามมาตรฐานที่เป็นลายลักษณ์อักษร ลักษณะการทำงานที่แน่นอนอาจขึ้นอยู่กับแฟล็กบรรทัดคำสั่งที่ให้มาในการเรียกใช้ ตัวอย่างเช่น ตัวประมวลผลล่วงหน้าของ GNU C สามารถทำให้เป็นไปตามมาตรฐานมากขึ้นโดยการจัดหาแฟล็กบางอย่าง [8]

คุณสมบัติพรีโปรเซสเซอร์เฉพาะคอมไพเลอร์

#pragmaสั่งเป็นสั่งคอมไพเลอร์ที่เฉพาะเจาะจงซึ่งผู้ขายคอมไพเลอร์อาจจะใช้เพื่อวัตถุประสงค์ของตนเอง ตัวอย่างเช่น#pragmaมักใช้เพื่ออนุญาตให้ระงับข้อความแสดงข้อผิดพลาดเฉพาะ จัดการการดีบักฮีปและสแต็ก และอื่นๆ คอมไพเลอร์ที่รองรับไลบรารีการทำให้ขนานOpenMPสามารถขนานforลูปโดยอัตโนมัติด้วย#pragma omp parallel for.

C99 แนะนำ#pragmaคำสั่งมาตรฐานสองสามคำสั่ง โดยใช้แบบฟอร์ม#pragma STDC ...ซึ่งใช้เพื่อควบคุมการใช้งานจุดลอยตัว _Pragma(...)นอกจากนี้ยังมีการเพิ่มรูปแบบอื่นที่คล้ายมาโครอีกด้วย

  • การใช้งานหลายอย่างไม่สนับสนุน trigraphs หรือไม่แทนที่โดยค่าเริ่มต้น
  • การใช้งานหลายอย่าง (รวมถึง เช่น คอมไพเลอร์ C โดย GNU, Intel, Microsoft และ IBM) ให้คำสั่งที่ไม่ได้มาตรฐานในการพิมพ์ข้อความเตือนในเอาต์พุต แต่ไม่หยุดกระบวนการคอมไพล์ การใช้งานทั่วไปคือการเตือนเกี่ยวกับการใช้รหัสเก่า ซึ่งขณะนี้เลิกใช้แล้วและรวมไว้เพื่อเหตุผลด้านความเข้ากันได้เท่านั้น เช่น:
    // GNU, Intel และ IBM 
    #warning "อย่าใช้ ABC ซึ่งเลิกใช้แล้ว ใช้ XYZ แทน"
    
    // Microsoft 
    #pragma message("อย่าใช้ ABC ซึ่งเลิกใช้แล้ว ใช้ XYZ แทน")
    
  • ตัวประมวลผลล่วงหน้าUnixบางตัวมี "การยืนยัน" ซึ่งมีความคล้ายคลึงกันเล็กน้อยกับการยืนยันที่ใช้ในการเขียนโปรแกรม [9]
  • GCC จัดให้มี#include_nextการผูกมัดส่วนหัวที่มีชื่อเดียวกัน [10]
  • ตัวประมวลผลล่วงหน้าObjective-Cมี#importซึ่งเหมือน#includeแต่รวมไฟล์เพียงครั้งเดียว pragma ผู้ขายทั่วไปกับฟังก์ชันการทำงานที่คล้ายกันในซีเป็น#pragma ครั้งเดียว

การใช้งานอื่นๆ

เนื่องจากตัวประมวลผลล่วงหน้า C สามารถเรียกใช้แยกต่างหากจากคอมไพเลอร์ที่มีให้มา จึงสามารถใช้แยกกันได้ในภาษาต่างๆ ตัวอย่างที่เด่น ได้แก่ การใช้งานในตอนนี้เลิกimakeระบบและ preprocessing Fortranอย่างไรก็ตาม การใช้ตัวประมวลผลล่วงหน้าสำหรับวัตถุประสงค์ทั่วไปนั้นถูกจำกัด: ภาษาที่ป้อนต้องมีลักษณะเหมือน C เพียงพอ[8] GNU Fortranเรียบเรียงโดยอัตโนมัติเรียกว่า "แบบดั้งเดิม" (ดูด้านล่าง) CPP ก่อนที่จะรวบรวมรหัส Fortran ถ้านามสกุลไฟล์บางอย่างมีการใช้[11] Intel ขอเสนอตัวประมวลผลล่วงหน้าของ Fortran, fpp สำหรับใช้กับคอมไพเลอร์ifortซึ่งมีความสามารถคล้ายกัน(12)

CPP ยังใช้งานได้ดีกับภาษาแอสเซมบลีส่วนใหญ่และภาษาที่คล้ายอัลกอล สิ่งนี้ต้องการให้ไวยากรณ์ภาษาไม่ขัดแย้งกับไวยากรณ์ CPP ซึ่งหมายความว่าไม่มีบรรทัดที่ขึ้นต้นด้วย#และเครื่องหมายอัญประกาศคู่นั้นซึ่ง cpp ตีความว่าเป็นตัวอักษรสตริงและละเว้น จึงไม่มีความหมายเชิงวากยสัมพันธ์อื่นนอกเหนือจากนั้น "โหมดดั้งเดิม" (ซึ่งทำหน้าที่เหมือนตัวประมวลผลล่วงหน้า ISO C ล่วงหน้า) โดยทั่วไปจะอนุญาตมากกว่าและเหมาะสมกว่าสำหรับการใช้งานดังกล่าว[13]ตัวแปรที่ยืดหยุ่นกว่าของตัวประมวลผลล่วงหน้า C ที่เรียกว่า GPPเป็นที่ต้องการสำหรับกรณีที่ซับซ้อนมากขึ้น[14]

ตัวประมวลผลล่วงหน้า C ไม่ใช่Turing-completeแต่มาใกล้มาก: สามารถระบุการคำนวณแบบเรียกซ้ำได้ แต่มีขอบเขตบนคงที่ตามจำนวนการเรียกซ้ำที่ดำเนินการ [15]อย่างไรก็ตาม ตัวประมวลผลล่วงหน้า C ไม่ได้ออกแบบมาให้ใช้งานได้ดีเท่ากับภาษาโปรแกรมเอนกประสงค์ ในฐานะที่เป็น preprocessor C ไม่ได้มีคุณสมบัติบาง preprocessors อื่น ๆ เช่นแมโครเวียนเกิดการขยายตัวของการคัดเลือกตาม quoting และการประเมินผลสตริงในเงื่อนไขก็จะถูก จำกัด มากเมื่อเทียบกับหน่วยประมวลผลแมโครทั่วไปมากขึ้นเช่นM4

ดูเพิ่มเติม

อ้างอิง

  1. ^ การประมวลผลข้อความล่วงหน้าสำหรับวัตถุประสงค์ทั่วไปด้วยตัวประมวลผลล่วงหน้า C เนื้อเรื่อง JavaScript
  2. ^ ริตชี่ (1993)
  3. ^ "Bell SAP – SAP พร้อมมาโครแบบมีเงื่อนไขและแบบเรียกซ้ำ" . HOPL: ออนไลน์ประวัติศาสตร์สารานุกรมภาษาโปรแกรม
  4. ^ รายการมาโครการใช้งาน ANSI C และ Microsoft C++ ที่กำหนดไว้ล่วงหน้า
  5. ^ เวอร์ซีเนียส, ลาร์ส. C "เคล็ดลับตัวประมวลผลล่วงหน้าสำหรับการใช้ประเภทข้อมูลที่คล้ายกัน" สืบค้นเมื่อ 9 มกราคม 2011
  6. ^ เมเยอร์ส, แรนดี้ (พฤษภาคม 2001) "มาโคร C: X ใหม่" . ดร. Dobb ของวารสาร สืบค้นเมื่อ1 พฤษภาคม 2551 .
  7. ^ บีล สเตฟาน (สิงหาคม 2547) "ซุปเปอร์มาโคร" . สืบค้นเมื่อ27 ตุลาคม 2551 . Cite journal requires |journal= (help)
  8. ^ a b "ตัวประมวลผลล่วงหน้าC: ภาพรวม" สืบค้นเมื่อ17 กรกฎาคม 2559 .
  9. ^ GCC คุณสมบัติล้าสมัย
  10. ^ https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html
  11. ^ "1.3 การประมวลผลล่วงหน้าและการรวบรวมตามเงื่อนไข" gnu.org
  12. ^ "การใช้ FPP Preprocessor" อินเทล. สืบค้นเมื่อ14 ตุลาคม 2558 .
  13. ^ "ภาพรวม (ตัวประมวลผลล่วงหน้าC)" . gcc.gnu.org ต้องบอกว่าคุณมักจะเลิกใช้ cpp กับสิ่งที่ไม่ใช่ C ภาษาโปรแกรม Algol-ish อื่น ๆ มักจะปลอดภัย (Ada ฯลฯ ) ดังนั้นการประกอบด้วยความระมัดระวัง โหมด -traditional-cpp จะคงพื้นที่สีขาวไว้มากกว่า และอนุญาตมากกว่า ปัญหามากมายสามารถหลีกเลี่ยงได้โดยการเขียนความคิดเห็นสไตล์ C หรือ C++ แทนความคิดเห็นในภาษาท้องถิ่น และทำให้มาโครเรียบง่าย
  14. ^ "GPP 2.23 — ตัวประมวลผลล่วงหน้าทั่วไป" . สืบค้นเมื่อ17 กรกฎาคม 2559 .
  15. ^ "ตัวประมวลผลล่วงหน้า C99 ทัวริงสมบูรณ์หรือไม่" . เก็บจากต้นฉบับเมื่อ 24 เมษายน 2559

ที่มา

ลิงค์ภายนอก