ทำไมต้องใช้ตัวแปร int สำหรับพินเมื่อ const int, enum หรือ #define เหมาะสมกว่า


24

เหตุใดผู้คนจึงใช้ตัวแปรเพื่อระบุหมายเลขพินเมื่อพินไม่น่าจะเปลี่ยนแปลงตลอดการประมวลผลของรหัส?

หลายครั้งที่ฉันเห็นสิ่งintที่ใช้สำหรับการกำหนดพิน

int led = 13;

เมื่อมีการใช้ const int

const int led = 13;

หรือenumหรือ#define

#define LED 13

ทำให้รู้สึกมากขึ้น

มันก็ยิ่งในบทเรียนบนเว็บไซต์ Arduino ตัวอย่างเช่นคนแรกที่กวดวิชาที่คนส่วนใหญ่เรียกใช้การกะพริบตา

ผมอ่านบางที่เป็นที่ต้องการมากกว่าconst int #defineทำไมสิ่งนี้ไม่ได้รับการสนับสนุนตั้งแต่เริ่มต้นแทนที่จะปล่อยให้คนพัฒนานิสัยที่ไม่ดีตั้งแต่ต้น? ฉันสังเกตเห็นว่ามันกลับมาสักพักหนึ่ง แต่เมื่อเร็ว ๆ นี้มันเริ่มทำให้ฉันหงุดหงิด

หน่วยความจำ / / การประมวลผลคอมพิวเตอร์ฉลาดเป็นconst int, enumหรือสำหรับเรื่องที่#defineดีกว่าธรรมดาintเช่นหมกมุ่นอยู่กับหน่วยความจำน้อยที่เก็บไว้ในหน่วยความจำที่แตกต่างกัน (Flash, EEPROM, SRAM) การดำเนินการได้เร็วขึ้นเร็วขึ้นเพื่อรวบรวม?


สิ่งนี้อาจดูเหมือนซ้ำซ้อนกับการใช้ #define หรือ const int สำหรับค่าคงที่หรือไม่ แต่ฉันกำลังพูดถึงคำถามที่ว่าทำไมผู้คนถึงใช้ตัวแปรและประสิทธิภาพการทำงานดีขึ้นอย่างไรเมื่อไม่ใช้แทนที่จะเป็นค่าคงที่แบบไหนดีกว่า


9
เพราะต้นกำเนิดแย่มาก มือสมัครเล่นส่วนใหญ่ไม่ได้เป็นโปรแกรมเมอร์ที่มีประสบการณ์และสอนนิสัยที่ไม่ดีให้กับคนอื่น
อิกนาซิโอ Vazquez อับราฮัม

1
ด้วยพินโดยเฉพาะรูปแบบง่าย ๆ ของฟังก์ชั่นพื้นฐาน arduino API เช่น digitalWrite ไม่สนับสนุนการออกแบบที่เหมาะสมเช่นการใช้มาสก์และที่อยู่หน่วยความจำเดียวสำหรับพอร์ตทั้งหมด
crasic

คำตอบ:


20
const int led = 13;

นั่นคือวิธีที่ถูกต้อง หรือแม้กระทั่ง:

const byte led = 13;

คุณมีพินกี่อัน?

บทเรียนบางบทไม่ผ่านการควบคุมคุณภาพเท่าที่ควร

ประสิทธิภาพจะดีขึ้นเมื่อใช้const byteเปรียบเทียบกับintคอมไพเลอร์อาจฉลาดพอที่จะรู้ว่าคุณกำลังทำอะไรอยู่

สิ่งที่คุณสามารถทำได้คือกระตุ้นให้ผู้คนใช้เทคนิคที่มีประสิทธิภาพมากขึ้นโดยใช้พวกเขาในรหัสของคุณเอง


ตอบสนองต่อความคิดเห็น

  1. ผู้แสดงความคิดเห็นแนะนำว่าbyteไม่เป็นมาตรฐาน C นี่ถูกต้อง แต่นี่เป็นเว็บไซต์ Arduino StackExchange และฉันเชื่อว่าการใช้ประเภทมาตรฐานที่จัดทำโดย Arduino IDE นั้นเป็นที่ยอมรับ

    ใน Arduino.h มีบรรทัดนี้:

    typedef uint8_t byte;

    โปรดทราบว่านี่ไม่เหมือนกันunsigned charทุกประการ ดูuint8_t เทียบกับถ่านที่ไม่ได้ลงชื่อและuint8_t char ถ่านที่ไม่ได้ลงชื่อเมื่อใด .

  2. ผู้แสดงความคิดเห็นคนอื่นแนะนำว่าการใช้ไบต์จะไม่จำเป็นต้องปรับปรุงประสิทธิภาพเนื่องจากตัวเลขที่เล็กกว่าintจะได้รับการเลื่อนขั้นเป็นint(ดูกฎการส่งเสริมการขายแบบจำนวนเต็มหากคุณต้องการข้อมูลเพิ่มเติม)

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

    00000086 <loop>:
      86:   8d e0           ldi r24, 0x0D   ; 13
      88:   61 e0           ldi r22, 0x01   ; 1
      8a:   1b d1           rcall   .+566       ; 0x2c2 <digitalWrite>

    ในความเป็นจริงมันสร้างรหัสเดียวกันไม่ว่าจะเป็น13:

    • เป็นตัวอักษร
    • คือ #define
    • คือ const int
    • คือ const byte

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


ข้อความแสดงข้อผิดพลาดที่สับสน

อีกเหตุผลสำคัญที่ควรหลีกเลี่ยง#defineคือข้อความแสดงข้อผิดพลาดที่คุณได้รับหากคุณทำผิดพลาด พิจารณาภาพร่าง "กะพริบ" ซึ่งมีข้อผิดพลาด:

#define LED = 13;

void setup() {
  pinMode(LED, OUTPUT);      // <---- line with error
}

void loop() {
  digitalWrite(LED, HIGH);   // <---- line with error 
  delay(1000);             
  digitalWrite(LED, LOW);    // <---- line with error
  delay(1000);              
}

บนพื้นผิวมันก็โอเค แต่มันก็สร้างข้อความผิดพลาดขึ้นมา:

Blink.ino: In function ‘void setup()’:
Blink:4: error: expected primary-expression before ‘=’ token
Blink:4: error: expected primary-expression before ‘,’ token
Blink:4: error: expected `;' before ‘)’ token
Blink.ino: In function ‘void loop()’:
Blink:8: error: expected primary-expression before ‘=’ token
Blink:8: error: expected primary-expression before ‘,’ token
Blink:8: error: expected `;' before ‘)’ token
Blink:10: error: expected primary-expression before ‘=’ token
Blink:10: error: expected primary-expression before ‘,’ token
Blink:10: error: expected `;' before ‘)’ token

คุณดูที่บรรทัดที่ไฮไลต์แรก (บรรทัดที่ 4) และไม่เห็นแม้แต่สัญลักษณ์ "=" นอกจากนี้เส้นดูดี ตอนนี้มันค่อนข้างชัดเจนว่าปัญหาคืออะไรที่นี่ ( = 13กำลังถูกแทนที่ด้วยLED) แต่เมื่อบรรทัดคือ 400 บรรทัดต่อไปในรหัสมันไม่ชัดเจนว่าปัญหาคือวิธีการกำหนด LED

ฉันเคยเห็นคนตกหลุมรักมาหลายครั้งแล้ว (รวมถึงตัวฉันด้วย)


คุณมีพินกี่อัน? เป็นจุดที่ดีมาก Nick เนื่องจากบอร์ดส่วนใหญ่มีเฉพาะในช่วงสิบไม่นับร้อย (เช่นมากกว่า 255) ดังนั้นintoverkill คือ ... นั่นคือจนกระทั่ง Arduino ในที่สุดก็ออกมาพร้อมกับบอร์ดTera ... :-)
Greenonline

2
C ไม่ได้byteชนิด unsigned charคุณหมายถึง
Kevin

ผลการดำเนินงานจะไม่จำเป็นต้องดีขึ้นด้วยbyteแทนintเนื่องจากในบริบทที่สุดจำนวนเต็มค่าที่มีชนิดที่มีขนาดเล็กกว่าจะเลื่อนตำแหน่งให้เป็นint int
Pete Becker

1
C doesn't have a byte type. You mean unsigned char.- คำตอบของฉันคือในบริบท Arduino typedef uint8_t byte;ซึ่งมีนี้ ดังนั้นสำหรับ Arduino การใช้byteก็โอเค
Nick Gammon

Performance won't necessarily be better with byte instead of int- ดูโพสต์แก้ไข
Nick Gammon

19

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

รหัส Arduino และแบบฝึกหัด Arduino ส่วนใหญ่เขียนโดยผู้ที่ไม่เคยมีการฝึกอบรมเรื่องการเขียนโปรแกรมมาก่อนและมีการ "เรียนรู้ด้วยตนเอง" จากแหล่งข้อมูลโดยผู้ที่ตัวเองเป็นคนสอนตัวเองอย่างมาก

ตัวอย่างโค้ดสำหรับการกวดวิชาจำนวนมากที่ฉันเห็นรอบ ๆ สถานที่ (และโดยเฉพาะอย่างยิ่งที่มีเฉพาะในวิดีโอ YouTube --- urgh) จะเป็นเครื่องหมายล้มเหลวหากฉันทำเครื่องหมายพวกเขาในการสอบ

ใช่constเป็นที่ต้องการมากกว่าไม่ใช่ const และแม้กระทั่งมากกว่า#defineเพราะ:

  • A const(เช่น a #defineซึ่งแตกต่างจาก non-const) ไม่ได้จัดสรร RAM ใด ๆ
  • A const(เช่นไม่ใช่ const แต่แตกต่างจาก a #define) ให้ค่าประเภทที่ชัดเจน

จุดที่สองมีความสนใจเป็นพิเศษ เว้นแต่จะระบุไว้เป็นอย่างอื่นอย่างชัดเจนด้วยการฝังตัวการพิมพ์ ( (long)3) หรือคำต่อท้ายประเภท ( 3L) หรือการปรากฏตัวของจุดทศนิยม ( 3.0) #defineตัวเลขหนึ่งจะเป็นจำนวนเต็มเสมอและคณิตศาสตร์ทั้งหมดที่ดำเนินการกับค่านั้นจะเหมือนกับว่ามันเป็น จำนวนเต็ม. เวลาส่วนใหญ่ที่ไม่ใช่ปัญหา แต่คุณสามารถเรียกใช้ในสถานการณ์ที่น่าสนใจเมื่อคุณลอง#defineค่าที่มากกว่าจำนวนเต็มสามารถเก็บเช่น#define COUNT 70000จากนั้นทำการดำเนินการทางคณิตศาสตร์กับintค่าอื่น ๆ โดยใช้constคุณจะได้รับการบอกคอมไพเลอร์ "ค่านี้จะถือว่าเป็นตัวแปรประเภท" - ดังนั้นคุณจะใช้แทน: const long count = 70000;และทั้งหมดจะทำงานตามที่คาดไว้

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


7
เป็นที่น่าสังเกตว่าconstตัวแปรอาจต้องการ RAM ขึ้นอยู่กับบริบทเช่นถ้ามันถูกกำหนดค่าเริ่มต้นโดยใช้ค่าส่งคืนจากฟังก์ชันที่ไม่ใช่ constexpr
Peter Bloomfield

ในทำนองเดียวกันแน่นอนจะต้องมีการรวบรวมเพื่อจัดสรรหน่วยความจำที่เกิดขึ้นจริงconst int foo = 13; bar(&foo); foo
Ilmari Karonen

3
หากคุณกำหนดแมโครที่ขยายเป็นค่าที่ไม่พอดีกับintคอมไพเลอร์จะถือว่าค่านั้นมีประเภทที่เล็กที่สุดซึ่งจะพอดี (กฎแบบโมดูโลเกี่ยวกับการเซ็นชื่อกับไม่ได้ลงนาม) หากคุณอยู่ในระบบที่intเป็น 16 บิต#define count 70000จะมีผลในcountการมองเช่นเช่นเดียวกับถ้ามันได้รับการกำหนดให้เป็นlong const long count = 70000;นอกจากนี้หากคุณส่งเวอร์ชันใดเวอร์ชันหนึ่งcountไปยังฟังก์ชันที่คาดหวังintคอมไพเลอร์สติใด ๆ จะปฏิบัติต่อพวกมันเหมือนกัน
Pete Becker

1
ฉันเห็นด้วยกับ @PeteBecker - โครงสร้างคล้าย#define COUNT 70000ไม่ตัดทอนลงใน int แต่คอมไพเลอร์ถือว่ามันเป็นประเภทที่มีขนาดใหญ่พอที่จะเก็บหมายเลขนั้นได้ มันเป็นความจริงที่มันอาจจะไม่ชัดเจนเมื่อคุณใช้COUNTมันไม่ได้เป็น int แต่คุณสามารถพูดสิ่งเดียวกันเกี่ยวกับconst longต่อไป
Nick Gammon

2
"#define จะเป็นจำนวนเต็มเสมอ"นั่นไม่เป็นความจริง คุณกำลังใช้กฎของตัวอักษรจำนวนเต็มและใช้กับแมโครตัวประมวลผลล่วงหน้า มันเหมือนกับการเปรียบเทียบแอปเปิ้ลและเพลงป๊อป การแสดงออกCOUNTในตัวอย่างของคุณจะถูกแทนที่ก่อนการรวบรวมด้วยการแสดงออก70000ซึ่งมีประเภทที่กำหนดโดยกฎของตัวอักษรเช่นเดียวกับ2หรือ13Lหรือ4.0ถูกกำหนดโดยกฎของตัวอักษร ความจริงที่ว่าคุณใช้#defineนามแฝงนิพจน์เหล่านั้นไม่เกี่ยวข้อง คุณสามารถใช้#defineนามแฝงของรหัส C ได้ตามต้องการ
การแข่งขัน Lightness กับโมนิก้า

2

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


0

คำตอบของฉันคือ ... พวกเขาทำเพราะใช้งานได้ ฉันมีเวลายากที่จะไม่ถามคำถามในคำตอบของฉันเช่น "ทำไมต้องเป็น 'ผิด'?


3
จุดเด่นหนึ่งของโปรแกรมเมอร์ที่ดีคือรหัสนั้นสะท้อนความตั้งใจของพวกเขาเสมอ
Ignacio Vazquez-Abrams

1
เรากำลังพูดถึง Arduinos ใช่มั้ย ;)
linhartr22

3
Arduino มีตัวแทนที่ไม่ดีในชุมชน EE ที่มีขนาดใหญ่กว่าเนื่องจากการออกแบบฮาร์ดแวร์ที่น่ากลัวจนเกินไปโดยชุมชน เราไม่ควรลองให้ sh * t เกี่ยวกับบางอย่างใช่ไหม
Ignacio Vazquez-Abrams

2
"โครงการส่วนใหญ่จะไม่เกี่ยวข้องกับความเสี่ยงต่อชีวิตหรือการเงิน ... " ไม่ต้องแปลกใจเลย ใครอยากมีส่วนร่วมกับ Arduino ที่มีโอกาสเสี่ยงหลังจากมองชุมชนโดยรวมแล้ว
Ignacio Vazquez-Abrams

2
มัน 'ผิด' ไม่ใช่เพราะมันไม่ทำงานในสถานการณ์เฉพาะอย่างใดอย่างหนึ่ง แต่เพราะเมื่อเทียบกับการทำในสิ่งที่ถูกต้องแล้วก็มีอีกหลายสถานการณ์ที่มันไม่ทำงาน ทำให้โค้ดนั้นบอบบาง การเปลี่ยนแปลงรหัสอาจทำให้เกิดความล้มเหลวอย่างลึกลับที่ทำให้เวลาในการดีบักหมดไป การตรวจสอบชนิดและข้อความแสดงข้อผิดพลาดของคอมไพเลอร์จะช่วยคุณตรวจจับข้อผิดพลาดประเภทต่าง ๆ ก่อนหน้านี้แทนที่จะเป็นในภายหลัง
Curt J. Sampson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.