ทำไมต้องกำหนดมาโครถ้ายังไม่ได้กำหนดไว้


93

ในฐานรหัส C ของเราฉันเห็นทุกมาโครที่กำหนดด้วยวิธีต่อไปนี้:

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

#ifndef BEEPTRIM_ROLL_RATE_DEGPS
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#endif

#ifndef FORCETRIMRELEASE_HOLD_TIME_MS
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#endif

#ifndef TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f
#endif

อะไรคือเหตุผลของการทำการตรวจสอบกำหนดเหล่านี้แทนที่จะเพียงแค่กำหนดมาโคร

#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f

ฉันไม่พบวิธีปฏิบัตินี้ที่อธิบายไว้ในเว็บ


6
การเปลี่ยนค่าคงที่ที่อื่นในโค้ดจะรับประกันได้ว่าจะทำงานในลักษณะนี้ หากที่อื่นมีผู้กำหนดมาโครเหล่านั้นมาโครเหล่านี้จะไม่ถูกเขียนทับโดยตัวประมวลผลล่วงหน้าเมื่อแยกวิเคราะห์ไฟล์นี้
Enzo Ferber

8
เป็นตัวอย่างของหลักการออกแบบ WET
สิ้นเชิง

โพสต์คำตอบพร้อมตัวอย่างลองรวบรวม
Enzo Ferber

คำตอบ:


141

สิ่งนี้ช่วยให้คุณสามารถแทนที่มาโครเมื่อคุณกำลังรวบรวม:

gcc -DMACRONAME=value

คำจำกัดความในไฟล์ส่วนหัวถูกใช้เป็นค่าเริ่มต้น


51

ดังที่ฉันได้กล่าวไว้ในความคิดเห็นลองนึกภาพสถานการณ์นี้:

foo.h

#define FOO  4

defs.h

#ifndef FOO
#define FOO 6
#endif

#ifndef BAR
#define BAR 4
#endif

bar.c

#include "foo.h"
#include "defs.h"

#include <stdio.h>

int main(void)
{
    printf("%d%d", FOO, BAR);
    return 0;
}

จะพิมพ์44.

อย่างไรก็ตามหากไม่มีเงื่อนไขifndefผลลัพธ์จะเป็นการรวบรวมคำเตือนของการกำหนดค่า MACRO ใหม่และจะพิมพ์ออก64มา

$ gcc -o bar bar.c
In file included from bar.c:2:0:
defs.h:1:0: warning: "FOO" redefined [enabled by default]
 #define FOO 6
 ^
In file included from bar.c:1:0:
foo.h:1:0: note: this is the location of the previous definition
 #define FOO 4
 ^

1
นี่คือคอมไพเลอร์เฉพาะ การกำหนดมาโครแบบออบเจ็กต์ใหม่นั้นผิดกฎหมายเว้นแต่ว่าการกำหนดนิยามใหม่จะเป็น "เหมือนกัน" (มีข้อกำหนดทางเทคนิคเพิ่มเติมสำหรับสิ่งนั้น แต่ไม่สำคัญที่นี่) รหัสที่ผิดกฎหมายต้องมีการวินิจฉัยและเมื่อออกคำเตือน (ที่นี่คำเตือน) คอมไพเลอร์มีอิสระที่จะทำอะไรก็ได้รวมถึงคอมไพล์โค้ดด้วยผลลัพธ์เฉพาะการนำไปใช้งาน
Pete Becker

7
หากคุณมีความขัดแย้ง defs สำหรับแมโครเดียวกันคุณจะไม่ค่อนข้างได้รับการเตือนในกรณีส่วนใหญ่หรือไม่ แทนที่จะใช้คำจำกัดความแรกแบบเงียบ ๆ (เนื่องจากคำจำกัดความที่ 2 ใช้ifdefเพื่อหลีกเลี่ยงการกำหนดนิยามใหม่)
Peter Cordes

@PeterCordes ส่วนใหญ่แล้วคำจำกัดความภายใต้#infdefs จะใช้เป็นค่า "ทางเลือก" หรือ "ค่าเริ่มต้น" โดยทั่วไป "หากผู้ใช้กำหนดค่าไว้ก็ใช้ได้ถ้าไม่เป็นเช่นนั้นให้ใช้ค่าเริ่มต้น"
Angew ไม่ภูมิใจใน SO

@Angew: ตกลงดังนั้นหากคุณมีบางส่วน#definesในส่วนหัวของไลบรารีที่เป็นส่วนหนึ่งของ ABI ของห้องสมุดคุณไม่ควรรวมเข้า#ifndefด้วยกัน (หรือดีกว่าให้ใช้enum) ฉันแค่อยากจะทำให้ชัดเจนว่า#ifndefเหมาะสมก็ต่อเมื่อมีคำจำกัดความที่กำหนดเองสำหรับบางสิ่งในหน่วยการรวบรวมหนึ่ง แต่ไม่เป็นไร หากa.cรวมส่วนหัวในลำดับที่แตกต่างไปจากb.cนั้นอาจได้รับนิยามที่แตกต่างกันmax(a,b)และหนึ่งในคำจำกัดความเหล่านั้นอาจแตกด้วยmax(i++, x)แต่อีกคำหนึ่งอาจใช้ชั่วคราวในนิพจน์คำสั่ง GNU ยังสับสนไม่น้อย!
Peter Cordes

@PeterCordes สิ่งที่ฉันชอบทำในกรณีนั้นคือ#ifdef FOO #error FOO already defined! #endif #define FOO x
โคลจอห์นสัน

17

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

ตัวอย่างเช่นใน g ++ คุณสามารถใช้-Dแฟล็กระหว่างการคอมไพล์เพื่อส่งผ่านค่าไปยังมาโคร


14

สิ่งนี้ทำเพื่อให้ผู้ใช้ไฟล์ส่วนหัวสามารถลบล้างนิยามจากโค้ดของเขา / เธอหรือจากแฟล็ก -D ของคอมไพเลอร์


7

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


2

คุณอาจนึกถึงกรอบงาน / ไลบรารีที่มอบค่าที่ตั้งไว้ล่วงหน้าให้กับผู้ใช้ซึ่งอนุญาตให้ผู้ใช้คอมไพล์และทำงานกับมัน คำจำกัดความเหล่านี้จะกระจายไปในไฟล์ต่างๆและผู้ใช้ขั้นสุดท้ายควรรวมไฟล์ config.h ไว้ซึ่งเขาสามารถกำหนดค่าได้ หากผู้ใช้ลืมกำหนดระบบสามารถทำงานต่อไปได้เนื่องจากค่าที่ตั้งไว้


1

การใช้

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

ช่วยให้ผู้ใช้ในการกำหนดค่าของแมโครโดยใช้อาร์กิวเมนต์บรรทัดคำสั่ง (ใน GCC / เสียงดังกราว / -DBEEPTRIM_PITCH_RATE_DEGPS=0.3fVS)

ยังมีอีกเหตุผลหนึ่งที่สำคัญ เป็นข้อผิดพลาดในการกำหนดมาโครตัวประมวลผลล่วงหน้าใหม่ให้แตกต่างกัน ดูคำตอบนี้คำถาม SO อีก หากไม่มีการ#ifndefตรวจสอบคอมไพลเลอร์ควรสร้างข้อผิดพลาดหาก-DBEEPTRIM_PITCH_RATE_DEGPS=0.3fใช้เป็นอาร์กิวเมนต์บรรทัดคำสั่งในการเรียกใช้คอมไพลเลอร์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.