ความแตกต่างระหว่าง const และ const ผันผวน


90

หากเราประกาศตัวแปรvolatileทุกครั้งที่มีการอัปเดตค่าใหม่
หากเราประกาศตัวแปรเป็นconstค่าของตัวแปรนั้นจะไม่มีการเปลี่ยนแปลง

แล้วconst volatile int temp;
อะไรคือการใช้ประกาศตัวแปรtempตามข้างบน?
จะเกิดอะไรขึ้นถ้าเราประกาศเป็นconst int temp?


คุณจะไม่ใช้const volatile int temp;ที่ขอบเขตการบล็อก (เช่นภายใน{ }) ไม่มีประโยชน์ที่นั่น
MM

คำตอบ:


149

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

volatileส่วนหนึ่งของการคัดเลือกวิธีการที่คอมไพเลอร์ไม่สามารถเพิ่มประสิทธิภาพหรือการเข้าถึงการสั่งซื้อไปยังวัตถุ

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

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

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

ตัวอย่างสั้น ๆ :

หากไม่ได้ทำเครื่องหมายคำชี้เหล่านี้ว่าvolatileมีปัญหาอาจเกิดขึ้นได้:

  • การทดสอบ while loop อาจอ่านการลงทะเบียนสถานะเพียงครั้งเดียวเนื่องจากคอมไพเลอร์สามารถสันนิษฐานได้ว่าสิ่งที่ชี้ไปจะไม่มีวันเปลี่ยนแปลง (ไม่มีอะไรในการทดสอบ while loop หรือการวนซ้ำที่สามารถเปลี่ยนแปลงได้) หากคุณเข้าสู่ฟังก์ชั่นในขณะที่ไม่มีอักขระรออยู่ในฮาร์ดแวร์ UART คุณอาจจบลงด้วยการวนซ้ำที่ไม่สิ้นสุดซึ่งไม่เคยหยุดนิ่งแม้ว่าจะได้รับอักขระก็ตาม
  • การอ่านการลงทะเบียนรับสามารถย้ายโดยคอมไพลเลอร์ไปก่อนลูป while - อีกครั้งเนื่องจากไม่มีอะไรในฟังก์ชันที่ระบุว่า*recv_regมีการเปลี่ยนแปลงโดยลูปไม่มีเหตุผลที่ไม่สามารถอ่านได้ก่อนเข้าสู่ลูป

ตัวกำหนดvolatileคุณสมบัติช่วยให้มั่นใจได้ว่าการเพิ่มประสิทธิภาพเหล่านี้ไม่ได้ดำเนินการโดยคอมไพลเลอร์


5
+1 สำหรับคำอธิบาย และฉันมีคำถาม: แล้ววิธีการระเหยของ const ล่ะ? หากฉันมีคลาสซึ่งเข้าถึงได้โดยเธรดจำนวนมาก (แม้ว่าการเข้าถึงจะซิงโครไนซ์กับ mutex) วิธีการ const ของฉันจะต้องมีความผันผวนเช่นกัน (เนื่องจากเธรดอื่นอาจเปลี่ยนตัวแปรบางตัวได้)
Sasa

41
  • volatile จะบอกคอมไพลเลอร์ว่าอย่าปรับโค้ดที่เกี่ยวข้องกับตัวแปรให้เหมาะสมโดยปกติเมื่อเรารู้ว่าสามารถเปลี่ยนจาก "ภายนอก" เช่นโดยเธรดอื่น
  • const จะบอกคอมไพเลอร์ว่าห้ามมิให้โปรแกรมแก้ไขค่าของตัวแปร
  • const volatileเป็นสิ่งที่พิเศษมากที่คุณอาจเห็นว่าใช้ 0 ครั้งในชีวิตของคุณ (tm) ตามที่คาดไว้หมายความว่าโปรแกรมไม่สามารถแก้ไขค่าของตัวแปรได้ แต่สามารถแก้ไขค่าจากภายนอกได้ดังนั้นจึงไม่มีการปรับให้เหมาะสมกับตัวแปร

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

2
แน่นอนว่าคุณพูดถูกจริงๆการแสดงมัลติเธรดเป็นเพียงตัวอย่างเดียว แต่ไม่ใช่เพียงตัวอย่างเดียว :)
mingos

26
หากคุณทำงานกับระบบฝังตัวคุณจะเห็นสิ่งนี้บ่อยมาก
Daniel Grillo

29

ไม่ใช่เพราะตัวแปรเป็น const ซึ่งอาจไม่มีการเปลี่ยนแปลงระหว่างจุดลำดับสองจุด

Constness คือสัญญาว่าคุณจะไม่เปลี่ยนค่าไม่ใช่ว่าค่าจะไม่เปลี่ยนแปลง


10
บวกหนึ่งสำหรับชี้ให้เห็นว่าconstข้อมูลไม่ใช่ "ค่าคงที่"
Bogdan Alexandru

7

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

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


7

ใน C constและvolatileเป็นตัวกำหนดประเภทและทั้งสองเป็นอิสระ

โดยทั่วไปconstหมายความว่าค่าไม่สามารถแก้ไขได้โดยโปรแกรม

และvolatileหมายความว่าค่าอาจมีการเปลี่ยนแปลงอย่างกะทันหัน (อาจมาจากภายนอกโปรแกรม)

ในความเป็นจริง C มาตรฐานให้เห็นตัวอย่างของการประกาศที่ถูกต้องซึ่งเป็นทั้งและconst volatileตัวอย่างคือ:

extern const volatile int real_time_clock;

ที่real_time_clockอาจได้รับการแก้ไขโดยฮาร์ดแวร์ แต่ไม่สามารถกำหนดให้, เพิ่มขึ้นหรือ decremented

ดังนั้นเราควรรักษาconstและvolatileแยกกันอยู่แล้ว รอบคัดเลือกประเภทเหล่านี้สามารถนำไปใช้กับstruct, union, enumและtypedefเช่นกัน


5

constหมายความว่าไม่สามารถแก้ไขตัวแปรด้วยรหัส c ไม่ใช่ว่าไม่สามารถเปลี่ยนแปลงได้ หมายความว่าไม่มีคำสั่งใดสามารถเขียนถึงตัวแปรได้ แต่ค่าของมันอาจยังเปลี่ยนแปลง

volatileหมายความว่าตัวแปรอาจเปลี่ยนแปลงได้ตลอดเวลาดังนั้นจึงไม่มีการใช้ค่าแคช การเข้าถึงตัวแปรแต่ละรายการจะต้องดำเนินการไปยังที่อยู่หน่วยความจำ

เนื่องจากคำถามถูกแท็ก "ฝังตัว" และสมมติว่าtempเป็นตัวแปรที่ผู้ใช้ประกาศไม่ใช่รีจิสเตอร์ที่เกี่ยวข้องกับฮาร์ดแวร์ (เนื่องจากโดยปกติจะจัดการในไฟล์. h แยกต่างหาก) ให้พิจารณา:

หน่วยประมวลผลแบบฝังตัวซึ่งมีทั้งหน่วยความจำข้อมูลแบบอ่าน - เขียนข้อมูล (RAM) และหน่วยความจำข้อมูลแบบอ่านอย่างเดียวที่ไม่ลบเลือนตัวอย่างเช่นหน่วยความจำ FLASH ในสถาปัตยกรรมฟอน - นอยมันน์ที่ข้อมูลและพื้นที่โปรแกรมใช้ข้อมูลร่วมกันและบัสแอดเดรส

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

ด้วยเหตุนี้:

int temp;เป็นตัวแปรที่เก็บไว้ใน RAM เริ่มต้นเป็น 0 เมื่อเริ่มต้น (cstart) อาจใช้ค่าแคช

const int temp;เป็นตัวแปรที่เก็บไว้ใน FLASH (อ่านอย่างเดียว) เริ่มต้นเป็น 0 ณ เวลาคอมไพเลอร์อาจใช้ค่าแคช

volatile int temp; เป็นตัวแปรที่เก็บไว้ใน RAM เริ่มต้นเป็น 0 เมื่อเริ่มต้น (cstart) ค่าที่แคชจะไม่ถูกใช้

const volatile int temp; เป็นตัวแปรที่เก็บไว้ใน FLASH (อ่านอย่างเดียว) เริ่มต้นเป็น 0 ณ เวลาคอมไพเลอร์ค่าแคชจะไม่ถูกใช้

นี่คือส่วนที่เป็นประโยชน์:

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

ตัวอย่างที่ใช้ได้จริงจะใช้tempสำหรับหมายเลขซีเรียลของอุปกรณ์ ในครั้งแรกที่โปรเซสเซอร์แบบฝังตัวทำงานtempจะเท่ากับ 0 (หรือค่าที่ประกาศ) และฟังก์ชันสามารถใช้ข้อเท็จจริงนี้เพื่อรันการทดสอบระหว่างการผลิตและหากประสบความสำเร็จขอให้กำหนดหมายเลขซีเรียลและแก้ไขค่าtempโดยวิธีการ ของฟังก์ชันพิเศษ โปรเซสเซอร์บางตัวมีช่วงแอดเดรสพิเศษพร้อมหน่วยความจำ OTP (โปรแกรมครั้งเดียว) สำหรับสิ่งนั้น

แต่นี่คือความแตกต่าง:

หากconst int tempเป็น ID ที่ปรับเปลี่ยนได้แทนที่จะเป็นหมายเลขซีเรียลที่ตั้งโปรแกรมได้ครั้งเดียวและไม่ได้ประกาศvolatileไว้อาจมีการใช้ค่าที่แคชไว้จนถึงการบูตครั้งต่อไปซึ่งหมายความว่า ID ใหม่อาจใช้ไม่ได้จนกว่าจะรีบูตครั้งถัดไปหรือแย่กว่านั้นคือบางฟังก์ชัน อาจใช้ค่าใหม่ในขณะที่ค่าอื่นอาจใช้ค่าแคชที่เก่ากว่าจนกว่าจะรีบูต หากconst int tempประกาศ IS voltaileการเปลี่ยนแปลง ID จะมีผลทันที


ว้าวคำตอบนี้ยาว

5

คุณสามารถใช้constและvolatileร่วมกัน ตัวอย่างเช่นหาก0x30ถือว่าเป็นค่าของพอร์ตที่เปลี่ยนแปลงโดยเงื่อนไขภายนอกเท่านั้นการประกาศต่อไปนี้จะป้องกันความเป็นไปได้ที่จะเกิดผลข้างเคียงโดยไม่ได้ตั้งใจ:



2

พูดง่ายๆคือค่าในตัวแปร 'const volatile' ไม่สามารถแก้ไขทางโปรแกรมได้ แต่สามารถแก้ไขได้โดยฮาร์ดแวร์ การระเหยในที่นี้คือการป้องกันการปรับให้เหมาะสมของคอมไพเลอร์


1

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

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