เหตุใดมาโครตัวประมวลผลล่วงหน้าจึงชั่วร้ายและทางเลือกอื่นคืออะไร?


96

ฉันถามเรื่องนี้มาตลอด แต่ไม่เคยได้รับคำตอบที่ดีเลย ฉันคิดว่าโปรแกรมเมอร์เกือบทุกคนก่อนที่จะเขียน "Hello World" คนแรกเคยพบวลีเช่น "มาโครไม่ควรใช้", "มาโครเป็นสิ่งชั่วร้าย" และอื่น ๆ คำถามของฉันคือ: ทำไม? ด้วย C ++ 11 ใหม่มีทางเลือกใหม่หลังจากผ่านไปหลายปีหรือไม่?

ส่วนที่ง่ายคือเกี่ยวกับมาโครเช่น#pragmaซึ่งเป็นแพลตฟอร์มเฉพาะและคอมไพเลอร์เฉพาะและส่วนใหญ่มักมีข้อบกพร่องร้ายแรงเช่น#pragma onceนี้เกิดข้อผิดพลาดได้ง่ายในสถานการณ์สำคัญอย่างน้อย 2 สถานการณ์: ชื่อเดียวกันในเส้นทางที่แตกต่างกันและด้วยการตั้งค่าเครือข่ายและระบบไฟล์บางอย่าง

แต่โดยทั่วไปแล้วมาโครและทางเลือกอื่น ๆ ในการใช้งานล่ะ?


19
#pragmaไม่ใช่มาโคร
FooF

1
คำสั่งพรีโปรเซสเซอร์ @foof?
user1849534

6
@ user1849534: ใช่ว่ามันคืออะไร ... #pragmaและคำแนะนำเกี่ยวกับมาโครไม่ได้พูดคุยเกี่ยวกับ
Ben Voigt

1
คุณสามารถทำมากด้วยconstexpr, inlineฟังก์ชั่นและtemplatesแต่boost.preprocessorและchaosแสดงให้เห็นว่าแมโครมีสถานที่ของพวกเขา ไม่ต้องพูดถึงมาโครการกำหนดค่าสำหรับคอมไพเลอร์แพลตฟอร์ม ฯลฯ ที่แตกต่างกัน
Brandon

1
การทำสำเนาที่เป็นไปได้ของมาโคร C ++ มีประโยชน์เมื่อใด
Aaron McDaid

คำตอบ:


166

มาโครก็เหมือนกับเครื่องมืออื่น ๆ - ค้อนที่ใช้ในการฆาตกรรมไม่ใช่สิ่งชั่วร้ายเพราะเป็นค้อน มันเป็นความชั่วร้ายในวิธีที่บุคคลนั้นใช้มันในทางนั้น หากคุณต้องการตอกตะปูค้อนเป็นเครื่องมือที่สมบูรณ์แบบ

มีบางแง่มุมของมาโครที่ทำให้ "ไม่ดี" (ฉันจะขยายความในแต่ละด้านในภายหลังและแนะนำทางเลือกอื่น):

  1. คุณไม่สามารถแก้ไขข้อบกพร่องมาโคร
  2. การขยายมาโครอาจนำไปสู่ผลข้างเคียงที่แปลกประหลาด
  3. มาโครไม่มี "เนมสเปซ" ดังนั้นหากคุณมีมาโครที่ขัดแย้งกับชื่อที่ใช้ในที่อื่นคุณจะได้รับการแทนที่มาโครในที่ที่คุณไม่ต้องการซึ่งมักจะนำไปสู่ข้อความแสดงข้อผิดพลาดแปลก ๆ
  4. มาโครอาจส่งผลต่อสิ่งที่คุณไม่ทราบ

ขอขยายความเล็กน้อยที่นี่:

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

เปลี่ยน : ใช้enumหรือconst T

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

การเปลี่ยน : ใช้ฟังก์ชัน - อินไลน์หากจำเป็นต้อง "เร็ว" (แต่ระวังว่าอินไลน์มากเกินไปไม่ใช่เรื่องดี)

2) การขยายมาโครอาจมีผลข้างเคียงแปลก ๆ

ที่มีชื่อเสียงเป็นที่หนึ่งและการใช้#define SQUARE(x) ((x) * (x)) x2 = SQUARE(x++)ซึ่งนำไปสู่x2 = (x++) * (x++);ซึ่งแม้ว่าจะเป็นรหัสที่ถูกต้อง [1] ก็แทบจะไม่ใช่สิ่งที่โปรแกรมเมอร์ต้องการอย่างแน่นอน ถ้าเป็นฟังก์ชั่นมันจะดีที่จะทำ x ++ และ x จะเพิ่มขึ้นเพียงครั้งเดียว

อีกตัวอย่างหนึ่งคือ "if else" ในมาโครสมมติว่าเรามีสิ่งนี้:

#define safe_divide(res, x, y)   if (y != 0) res = x/y;

แล้ว

if (something) safe_divide(b, a, x);
else printf("Something is not set...");

มันกลายเป็นสิ่งที่ผิดไปโดยสิ้นเชิง ....

เปลี่ยน : ฟังก์ชั่นจริง

3) มาโครไม่มีเนมสเปซ

หากเรามีมาโคร:

#define begin() x = 0

และเรามีโค้ดบางอย่างใน C ++ ที่ใช้เริ่มต้น:

std::vector<int> v;

... stuff is loaded into v ... 

for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
   std::cout << ' ' << *it;

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

การแทนที่ : ไม่มีการแทนที่เป็น "กฎ" มากนัก - ใช้เฉพาะชื่อตัวพิมพ์ใหญ่สำหรับมาโครและห้ามใช้ชื่อตัวพิมพ์ใหญ่ทั้งหมดสำหรับสิ่งอื่น

4) มาโครมีเอฟเฟกต์ที่คุณไม่รู้

ใช้ฟังก์ชันนี้:

#define begin() x = 0
#define end() x = 17
... a few thousand lines of stuff here ... 
void dostuff()
{
    int x = 7;

    begin();

    ... more code using x ... 

    printf("x=%d\n", x);

    end();

}

ตอนนี้โดยไม่ต้องดูมาโครคุณจะคิดว่าการเริ่มต้นเป็นฟังก์ชันซึ่งไม่ควรส่งผลกระทบต่อ x

สิ่งนี้และฉันเคยเห็นตัวอย่างที่ซับซ้อนกว่านี้มากอาจทำให้วันของคุณยุ่งเหยิง!

การแทนที่ : อย่าใช้มาโครเพื่อตั้งค่า x หรือส่ง x เข้าเป็นอาร์กิวเมนต์

มีหลายครั้งที่การใช้มาโครเป็นประโยชน์อย่างแน่นอน ตัวอย่างหนึ่งคือการรวมฟังก์ชันด้วยมาโครเพื่อส่งต่อข้อมูลไฟล์ / บรรทัด:

#define malloc(x) my_debug_malloc(x, __FILE__, __LINE__)
#define free(x)  my_debug_free(x, __FILE__, __LINE__)

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

[1] เป็นพฤติกรรมที่ไม่ได้กำหนดไว้ในการอัปเดตหนึ่งตัวแปรมากกว่าหนึ่งครั้ง "ในจุดลำดับ" จุดลำดับไม่เหมือนกับคำสั่ง แต่สำหรับเจตนาและวัตถุประสงค์ส่วนใหญ่นั่นคือสิ่งที่เราควรพิจารณาว่าเป็น การทำเช่นx++ * x++นั้นจะอัปเดตxสองครั้งซึ่งไม่ได้กำหนดไว้และอาจนำไปสู่ค่าที่แตกต่างกันในระบบxต่างๆ


6
ปัญหาจะสามารถแก้ไขได้โดยการตัดภายในร่างกายแมโครif else do { ... } while(0)สิ่งนี้จะทำงานตามที่เราคาดหวังเกี่ยวกับifและforและปัญหาขั้นตอนการควบคุมอื่น ๆ ที่อาจมีความเสี่ยง แต่ใช่ว่าฟังก์ชันจริงมักจะเป็นทางออกที่ดีกว่า #define macro(arg1) do { int x = func(arg1); func2(x0); } while(0)
Aaron McDaid

11
@AaronMcDaid: ใช่มีวิธีแก้ปัญหาบางอย่างที่ช่วยแก้ปัญหาบางอย่างที่พบในมาโครเหล่านี้ ประเด็นทั้งหมดของโพสต์ของฉันไม่ได้อยู่ที่การแสดงวิธีทำมาโครให้ดี แต่ "การทำให้มาโครผิดพลาดง่ายแค่ไหน" ซึ่งมีทางเลือกที่ดี ที่กล่าวว่ามีหลายสิ่งที่มาโครแก้ไขได้ง่ายมากและมีหลายครั้งที่มาโครเป็นสิ่งที่ควรทำเช่นกัน
Mats Petersson

1
ในประเด็นที่ 3 ข้อผิดพลาดไม่ได้เป็นปัญหาอีกต่อไป คอมไพเลอร์สมัยใหม่เช่น Clang จะพูดทำนองnote: expanded from macro 'begin'และแสดงว่าbeginกำหนดไว้ที่ไหน
kirbyfan64sos

5
มาโครยากที่จะแปลเป็นภาษาอื่น
Marco van de Voort

1
@FrancescoDondi: stackoverflow.com/questions/4176328/… (คำตอบนั้นค่อนข้างสั้นมันพูดถึง i ++ * i ++ และอื่น ๆ
Mats Petersson

21

คำพูดที่ว่า "มาโครเป็นสิ่งชั่วร้าย" มักหมายถึงการใช้ #define ไม่ใช่ #pragma

โดยเฉพาะนิพจน์อ้างถึงสองกรณีนี้:

  • การกำหนดตัวเลขวิเศษเป็นมาโคร

  • ใช้มาโครเพื่อแทนที่นิพจน์

ด้วย C ++ 11 ใหม่มีทางเลือกใหม่หลังจากผ่านไปหลายปีหรือไม่?

ใช่สำหรับรายการในรายการด้านบน (ควรกำหนดตัวเลขมหัศจรรย์ด้วย const / constexpr และนิพจน์ควรกำหนดด้วยฟังก์ชัน [normal / inline / template / inline template]

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

  • เมื่อกำหนดมาโครสำหรับตัวเลขวิเศษคอมไพเลอร์จะไม่เก็บข้อมูลประเภทสำหรับค่าที่กำหนดไว้ ซึ่งอาจทำให้เกิดคำเตือนการคอมไพล์ (และข้อผิดพลาด) และทำให้ผู้คนสับสนในการดีบักโค้ด

  • เมื่อกำหนดมาโครแทนฟังก์ชันโปรแกรมเมอร์ที่ใช้โค้ดนั้นคาดหวังว่ามันจะทำงานเหมือนฟังก์ชันและจะไม่ทำงาน

พิจารณารหัสนี้:

#define max(a, b) ( ((a) > (b)) ? (a) : (b) )

int a = 5;
int b = 4;

int c = max(++a, b);

คุณคาดว่า a และ c จะเป็น 6 หลังจากการกำหนดให้กับ c (ตามที่ควรจะเป็นโดยใช้ std :: max แทนมาโคร) รหัสจะดำเนินการแทน:

int c = ( ((++a) ? (b)) ? (++a) : (b) ); // after this, c = a = 7

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

ซึ่งหมายความว่าหากคุณกำหนดมาโครด้านบน (สำหรับสูงสุด) คุณจะไม่สามารถ#include <algorithm>เขียนโค้ดใด ๆ ด้านล่างนี้ได้อีกต่อไปเว้นแต่คุณจะเขียนอย่างชัดเจน:

#ifdef max
#undef max
#endif
#include <algorithm>

การมีมาโครแทนตัวแปร / ฟังก์ชันหมายความว่าคุณไม่สามารถใช้ที่อยู่ของมันได้:

  • หากมาโครเป็นค่าคงที่ประเมินเป็นตัวเลขวิเศษคุณจะไม่สามารถส่งต่อไปตามที่อยู่ได้

  • สำหรับ macro-as-function คุณไม่สามารถใช้เป็นเพรดิเคตหรือใช้แอดเดรสของฟังก์ชันหรือถือว่าเป็น functor

แก้ไข: ดังตัวอย่างทางเลือกที่ถูกต้องจาก#define maxด้านบน:

template<typename T>
inline T max(const T& a, const T& b)
{
    return a > b ? a : b;
}

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

int a = 0;
double b = 1.;
max(a, b);

หากกำหนดค่าสูงสุดนี้เป็นมาโครโค้ดจะคอมไพล์ (พร้อมคำเตือน)

หากค่าสูงสุดนี้ถูกกำหนดให้เป็นฟังก์ชันเทมเพลตคอมไพเลอร์จะชี้ให้เห็นถึงความคลุมเครือและคุณต้องพูดอย่างใดอย่างหนึ่งmax<int>(a, b)หรือmax<double>(a, b)(ดังนั้นจึงระบุเจตนาของคุณอย่างชัดเจน)


1
ไม่จำเป็นต้องเป็น c ++ 11 เฉพาะ คุณสามารถใช้ฟังก์ชันเพื่อแทนที่การใช้มาโครเป็นนิพจน์และ [คงที่] const / constexpr เพื่อแทนที่การใช้งานมาโครเป็นค่าคงที่
utnapistim

1
แม้แต่ C99 ก็อนุญาตให้ใช้const int someconstant = 437;และสามารถใช้ได้เกือบทุกวิธีที่จะใช้มาโคร เช่นเดียวกันสำหรับฟังก์ชันขนาดเล็ก มีบางสิ่งที่คุณสามารถเขียนบางสิ่งเป็นมาโครที่ใช้ไม่ได้ในนิพจน์ทั่วไปใน C (คุณสามารถสร้างบางสิ่งที่หาค่าเฉลี่ยอาร์เรย์ของตัวเลขประเภทใดก็ได้ซึ่ง C ไม่สามารถทำได้ แต่ C ++ มีเทมเพลต สำหรับการที่). ในขณะที่ C ++ 11 จะเพิ่มสิ่งต่างๆอีกเล็กน้อยที่ "คุณไม่จำเป็นต้องใช้มาโครสำหรับสิ่งนี้" แต่ส่วนใหญ่แก้ไขแล้วใน C / C ++ ก่อนหน้านี้
Mats Petersson

การเพิ่มขึ้นล่วงหน้าในขณะที่ผ่านการโต้แย้งเป็นการฝึกเขียนโค้ดที่แย่มาก และใครก็ตามที่เขียนโค้ดใน C / C ++ ไม่ควรคิดว่าการเรียกที่เหมือนฟังก์ชันไม่ใช่มาโคร
StephenG

การนำไปใช้งานจำนวนมากวงเล็บตัวระบุโดยสมัครใจmaxและminหากตามด้วยวงเล็บด้านซ้าย แต่คุณไม่ควรกำหนดมาโครดังกล่าว ...
LF

14

ปัญหาทั่วไปคือ:

#define DIV(a,b) a / b

printf("25 / (3+2) = %d", DIV(25,3+2));

มันจะพิมพ์ 10 ไม่ใช่ 5 เพราะพรีโปรเซสเซอร์จะขยายด้วยวิธีนี้:

printf("25 / (3+2) = %d", 25 / 3 + 2);

เวอร์ชันนี้ปลอดภัยกว่า:

#define DIV(a,b) (a) / (b)

2
ตัวอย่างที่น่าสนใจโดยทั่วไปเป็นเพียงโทเค็นที่ไม่มีความหมาย
user1849534

ใช่. ขยายตามวิธีที่กำหนดให้กับมาโคร DIVแมโครอาจจะเขียนใหม่กับคู่ของ () bรอบ
Phaazon

2
คุณหมายถึง#define DIV(a,b)ไม่ใช่#define DIV (a,b)ซึ่งแตกต่างกันมาก
rici

6
#define DIV(a,b) (a) / (b)ไม่ดีพอ ตามหลักปฏิบัติทั่วไปให้เพิ่มวงเล็บด้านนอกสุดเช่นนี้เสมอ:#define DIV(a,b) ( (a) / (b) )
PJTraill

3

มาโครมีค่าโดยเฉพาะอย่างยิ่งสำหรับการสร้างโค้ดทั่วไป (พารามิเตอร์ของมาโครอาจเป็นอะไรก็ได้) บางครั้งก็มีพารามิเตอร์

เพิ่มเติมมีการวางโค้ดนี้ (เช่นแทรก) ไว้ที่จุดของมาโคร

OTOH ผลลัพธ์ที่คล้ายกันอาจทำได้ด้วย:

  • ฟังก์ชั่นที่มากเกินไป (ประเภทพารามิเตอร์ต่างๆ)

  • เทมเพลตใน C ++ (ประเภทและค่าพารามิเตอร์ทั่วไป)

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

แก้ไข: สาเหตุที่มาโครไม่ดี:

1) ไม่มีการตรวจสอบประเภทของอาร์กิวเมนต์ (ไม่มีประเภท) จึงสามารถนำไปใช้ในทางที่ผิดได้ง่าย 2) บางครั้งขยายเป็นโค้ดที่ซับซ้อนมากซึ่งอาจระบุและเข้าใจได้ยากในไฟล์ที่ประมวลผลล่วงหน้า 3) เกิดข้อผิดพลาดได้ง่าย -prone code ในมาโครเช่น:

#define MULTIPLY(a,b) a*b

แล้วโทร

MULTIPLY(2+3,4+5)

ที่ขยายใน

2 + 3 * 4 + 5 (และไม่เข้า: (2 + 3) * (4 + 5))

ในการมีอย่างหลังคุณควรกำหนด:

#define MULTIPLY(a,b) ((a)*(b))

3

ฉันไม่คิดว่าการใช้คำจำกัดความของตัวประมวลผลล่วงหน้าหรือมาโครจะมีอะไรผิดปกติอย่างที่คุณเรียกมัน

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


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

3

มาโครใน C / C ++ สามารถใช้เป็นเครื่องมือสำคัญสำหรับการควบคุมเวอร์ชัน สามารถส่งรหัสเดียวกันไปยังไคลเอนต์สองเครื่องด้วยการกำหนดค่ามาโครเล็กน้อย ฉันใช้สิ่งต่างๆเช่น

#define IBM_AS_CLIENT
#ifdef IBM_AS_CLIENT 
  #define SOME_VALUE1 X
  #define SOME_VALUE2 Y
#else
  #define SOME_VALUE1 P
  #define SOME_VALUE2 Q
#endif

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


การตั้งค่ามาโครบน cmdline ระหว่างการคอมไพล์เพื่อสร้างตัวแปรสองตัวจากโค้ดเบสเดียวนั้นดีมาก ในการดูแล
kevinf

1
จากมุมมองบางประการการใช้งานนี้เป็นสิ่งที่อันตรายที่สุด: เครื่องมือ (IDEs, เครื่องวิเคราะห์แบบคงที่, การปรับโครงสร้างใหม่) จะมีช่วงเวลาที่ยากลำบากในการหาเส้นทางรหัสที่เป็นไปได้
erenon

1

ฉันคิดว่าปัญหาคือมาโครไม่ได้รับการปรับให้เหมาะสมโดยคอมไพเลอร์และ "น่าเกลียด" ในการอ่านและแก้ไขข้อบกพร่อง

บ่อยครั้งทางเลือกที่ดีคือฟังก์ชันทั่วไปและ / หรือฟังก์ชันอินไลน์


2
อะไรทำให้คุณเชื่อว่ามาโครไม่ได้รับการปรับให้เหมาะสม เป็นการทดแทนข้อความอย่างง่ายและผลลัพธ์จะได้รับการปรับให้เหมาะสมเช่นเดียวกับโค้ดที่เขียนโดยไม่มีมาโคร
Ben Voigt

@BenVoigt แต่พวกเขาไม่ได้พิจารณาความหมายและสิ่งนี้อาจนำไปสู่สิ่งที่สามารถพิจารณาได้ว่า "ไม่เหมาะสม" ... อย่างน้อยนี่ก็เป็นครั้งแรกที่ฉันคิดเกี่ยวกับstackoverflow.com/a/14041502/1849534
user1849534

1
@ user1849534: นั่นไม่ใช่ความหมายของคำว่า "Optimized" ในบริบทของการรวบรวม
Ben Voigt

1
@BenVoigt แน่นอนมาโครเป็นเพียงการแทนที่ข้อความ คอมไพเลอร์แค่โค้ดซ้ำไม่ใช่ปัญหาเรื่องประสิทธิภาพ แต่สามารถเพิ่มขนาดโปรแกรมได้ โดยเฉพาะอย่างยิ่งในบางบริบทที่คุณมีข้อ จำกัด ขนาดโปรแกรม บางโค้ดเต็มไปด้วยมาโครจนขนาดของโปรแกรมเป็นสองเท่า
Davide Icardi

1

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

  • การสร้างซอฟต์แวร์รุ่นเดียวกันที่แตกต่างกันโดยใช้โครงสร้างประเภท #ifdef ตัวอย่างเช่นการเปิดตัว windows สำหรับภูมิภาคต่างๆ
  • สำหรับการกำหนดค่าที่เกี่ยวข้องกับการทดสอบโค้ด

ทางเลือก - หนึ่งสามารถใช้ไฟล์การกำหนดค่าบางประเภทในรูปแบบ ini, xml, json เพื่อวัตถุประสงค์ที่คล้ายคลึงกัน แต่การใช้สิ่งเหล่านี้จะมีผลต่อเวลาทำงานกับโค้ดซึ่งมาโครตัวประมวลผลล่วงหน้าสามารถหลีกเลี่ยงได้


1
เนื่องจาก C ++ 17 constexpr ถ้า + ไฟล์ส่วนหัวที่มีตัวแปร constexpr "config" สามารถแทนที่ # ifdef ได้
Enhex
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.