การเยื้อง #defines


99

ฉันรู้ว่า#defines ฯลฯ ปกติไม่เคยเยื้อง ทำไม?

ฉันกำลังทำงานกับโค้ดบางตัวในขณะนี้ซึ่งมีส่วนผสมที่น่ากลัวของ#defines, #ifdefs, #elses, #endifs และอื่น ๆ ทั้งหมดนี้มักจะผสมกับรหัส C ปกติ การไม่เยื้อง#defines ทำให้อ่านยาก และส่วนผสมของโค้ดเยื้องกับ#defines ที่ไม่เยื้องเป็นฝันร้าย

ประโยชน์ของการไม่เยื้อง#defines คืออะไร? มันทำให้ฉันเป็นคนไม่ดีหรือเปล่าถ้าฉันเยื้องย่างเข้าไป นี่ไม่ดีกว่านี้เหรอ?

#ifdef SDCC
    #if DEBUGGING == 1
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x3DC0
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x7DC0
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #else
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x4000
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x8000
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #endif
#else
    #if DEBUGGING == 1
        #define FLASH_MEMORY_END 0x7DC0
    #else
        #define FLASH_MEMORY_END 0x8000
    #endif
#endif

คำตอบ:


103

พรีโปรเซสเซอร์ Pre-ANSI C ไม่อนุญาตให้มีช่องว่างระหว่างจุดเริ่มต้นของบรรทัดและอักขระ "#" "#" ที่นำหน้าจะต้องอยู่ในคอลัมน์แรกเสมอ

คอมไพเลอร์ Pre-ANSI C ไม่มีอยู่จริงในทุกวันนี้ ใช้รูปแบบที่เคย (เว้นวรรคก่อน "#" หรือเว้นวรรคระหว่าง "#" และตัวระบุ) ที่คุณต้องการ

http://www.delorie.com/gnu/docs/gcc/cpp_48.html


26

ดังที่บางคนได้กล่าวไปแล้วคอมไพเลอร์ Pre-ANSI บางตัวต้องการให้ # เป็นอักขระตัวแรกในบรรทัด แต่ไม่ต้องการให้เชื่อมต่อกับคำสั่งของตัวประมวลผลล่วงหน้าดังนั้นการเยื้องจึงทำด้วยวิธีนี้

#ifdef SDCC
#  if DEBUGGING == 1
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x3DC0
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x7DC0
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  else
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x4000
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x8000
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  endif
#else
#  if DEBUGGING == 1
#    define FLASH_MEMORY_END 0x7DC0
#  else
#    define FLASH_MEMORY_END 0x8000
#  endif
#endif

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


16

เกี่ยวกับการแยกวิเคราะห์คำสั่งของตัวประมวลผลก่อนมาตรฐาน C99 (และมาตรฐาน C89 ก่อนหน้านั้น) มีความชัดเจนเกี่ยวกับลำดับของการดำเนินการที่ดำเนินการอย่างมีเหตุผลโดยคอมไพเลอร์ โดยเฉพาะอย่างยิ่งฉันเชื่อว่ามันหมายความว่ารหัสนี้:

/* */ # /* */ include /* */ <stdio.h> /* */

เทียบเท่ากับ:

#include <stdio.h>

เพื่อให้ดีขึ้นหรือแย่ลง GCC 3.4.4 ที่มี '-std = c89 -pedantic' ยอมรับบรรทัดที่แสดงความคิดเห็นไม่ว่าจะในอัตราใดก็ตาม ฉันไม่สนับสนุนสิ่งนั้นในรูปแบบ - ไม่ใช่สำหรับวินาที (มันน่ากลัว) ฉันแค่คิดว่ามันเป็นไปได้

ISO / IEC 9899: 1999 ส่วน 5.1.1.2 ขั้นตอนการแปลกล่าวว่า:

  1. [การแมปอักขระรวมถึงทริกเกอร์]

  2. [การต่อเส้น - การลบแบ็กสแลชขึ้นบรรทัดใหม่]

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

  4. คำสั่งก่อนการประมวลผลจะถูกเรียกใช้การเรียกมาโครจะถูกขยาย [... ]

ส่วน 6.10 คำสั่งก่อนการประมวลผลกล่าวว่า:

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

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

ดังที่คนอื่น ๆ ตั้งข้อสังเกตพรีโปรเซสเซอร์ C ก่อนมาตรฐานไม่ได้ทำงานอย่างสม่ำเสมอในหลาย ๆ วิธีและช่องว่างก่อนและในคำสั่งก่อนตัวประมวลผลเป็นหนึ่งในพื้นที่ที่คอมไพเลอร์ต่างกันทำสิ่งต่าง ๆ รวมถึงไม่รู้จักคำสั่งพรีโปรเซสเซอร์ที่มีช่องว่างข้างหน้า .

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


7

ไม่รู้ทำไมไม่ธรรมดามากกว่า มีหลายครั้งที่ฉันต้องการเยื้องคำสั่งพรีโปรเซสเซอร์

สิ่งหนึ่งที่ขวางทางฉัน (และบางครั้งก็ทำให้ฉันเลิกพยายาม) คือบรรณาธิการ / IDE จำนวนมากหรือส่วนใหญ่จะส่งคำสั่งไปที่คอลัมน์ 1 ด้วยการยั่วยุเพียงเล็กน้อย ซึ่งน่ารำคาญเหมือนนรก.


5

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

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

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif

14
สาเหตุที่รหัสนี้ดูแปลก ๆ เนื่องจากคุณได้สร้าง "สตรีม" ของการเยื้องสองรายการ ฉันจะเยื้องบรรทัด 4 อีกหนึ่งระดับและฉันจะเยื้องบรรทัดที่ 6 และ 7 อีกสองระดับ
Kevin Laity

3
เห็นด้วยอย่างสิ้นเชิง. บางครั้งฉันยังใส่เครื่องหมายวงเล็บเพื่อให้ # if ดูเหมือนกับ if
baash05

3
ฉันพยายามที่ยากมากที่จะจัดให้มีรหัสของฉันเพื่อให้มีไม่มี #ifdefเส้นในส่วนที่ฉันมีรหัสที่เกิดขึ้นจริง แต่ถ้าฉันต้องการสิ่งที่มีเงื่อนไขฉันจะใส่มันลงในฟังก์ชันหรือแยกส่วนมาโครออก มันชัดเจนกว่าที่ฉันพบ (อย่างน้อยก็สำหรับฉัน) ตามหลักการแล้วส่วนที่แยกออกมาทั้งหมดจะอยู่ในไฟล์อื่น ๆ (ส่วนหัวหรือไฟล์ต้นฉบับที่รวบรวมตามเงื่อนไข "เงื่อนไข" ตามปกติคือแพลตฟอร์มที่โค้ดถูกสร้างขึ้นสำหรับแพลตฟอร์ม)
Donal Fellows

2
ฉันจะเยื้องบรรทัด 4 หนึ่งระดับและบรรทัดที่ 6 และ 7 สองระดับ
Rocketmagnet

3

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

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

การแยกคำสั่งด้วยสายตาออกจากส่วนที่เหลือของโค้ดของคุณมีความสำคัญมากขึ้นเมื่อมีการสลับกับโค้ด (แทนที่จะเป็นบล็อกคำสั่งเฉพาะดังตัวอย่างที่คุณให้)


3
จากมุมมองของ IP อะไรคือความแตกต่างระหว่างสิ่งที่ไม่ได้คอมไพล์และสิ่งที่เข้าไม่ถึงเนื่องจาก jmp
baash05

2

ตอนนี้ฉันกำลังใช้โค้ดบางส่วนซึ่งมี #defines, #ifdefs, #elses, #endifs, #etc ผสมกันอย่างน่ากลัว ทั้งหมดนี้มักจะผสมกับรหัส C ปกติ การไม่เยื้อง #defines ทำให้อ่านยาก และการผสมผสานระหว่างรหัสเยื้องกับ #defines ที่ไม่เยื้องเป็นฝันร้าย

วิธีแก้ปัญหาทั่วไปคือการแสดงความคิดเห็นคำสั่งเพื่อให้คุณรู้ได้ง่ายว่าพวกเขาอ้างถึงอะไร:

#ifdef FOO
/* a lot of code */
#endif /* FOO */

#ifndef FOO
/* a lot of code */
#endif /* not FOO */

6
ฉันเคยเห็นรูปแบบนั้นเจ้านายของฉันใช้มัน และเช่นเดียวกับรหัสที่เหลือของเขามันก็ทำให้ยุ่งเหยิง ลองนึกภาพลบการเยื้องทั้งหมดออกจากคำสั่ง if () ปกติของคุณและใช้ความคิดเห็นเหล่านั้นแทน คุณจะบ่นว่าคุณไม่สามารถมองเห็นสิ่งที่พวกเขาอ้างถึงได้ง่ายๆ
Rocketmagnet

2

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


1
คำตอบที่ดี คุณสามารถปรับปรุงได้โดยเพิ่มการอ้างอิงคู่มือสไตล์ที่เฉพาะเจาะจงหรือไม่?
EtherDragon

0

ฉันรู้ว่านี่เป็นหัวข้อเก่า แต่ฉันเสียเวลาไปสองสามวันในการค้นหาวิธีแก้ปัญหา ฉันเห็นด้วยกับโพสต์แรกที่ตั้งใจจะทำให้โค้ดสะอาดขึ้นหากคุณมีจำนวนมาก (ในกรณีของฉันฉันใช้คำสั่งเพื่อเปิด / ปิดการบันทึกแบบละเอียด) ในที่สุดฉันก็พบโซลูชันที่นี่ซึ่งใช้งานได้กับ Visual Studio 2017

หากคุณต้องการเยื้องนิพจน์ #pragma คุณสามารถเปิดใช้งานภายใต้: เครื่องมือ> ตัวเลือก> ตัวแก้ไขข้อความ> C / C ++> การจัดรูปแบบ> การเยื้อง> ตำแหน่งของคำสั่งก่อนตัวประมวลผล> เว้นการเยื้อง

ปัญหาเดียวที่เหลือคือเค้าโครงรหัสอัตโนมัติแก้ไขการจัดรูปแบบ = (

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