กลับไปหน้าบทความ

อ่าน 15 นาที

const (การเขียนโปรแกรมคอมพิวเตอร์)

ใน ภาษาโปรแกรม บาง ภาษา const เป็น ตัวกำหนดประเภท ( คำหลัก ที่ใช้กับ ประเภทข้อมูล ) ที่ระบุว่าข้อมูลนั้นเป็นแบบอ่านอย่างเดียว ในขณะที่สามารถใช้ในการประกาศ ค่าคงที่ได้ แต่ const ใน...

const (การเขียนโปรแกรมคอมพิวเตอร์)

ในภาษาโปรแกรม บาง ภาษาconstเป็นตัวกำหนดประเภท ( คำหลักที่ใช้กับประเภทข้อมูล ) ที่ระบุว่าข้อมูลนั้นเป็นแบบอ่านอย่างเดียว ในขณะที่สามารถใช้ในการประกาศค่าคงที่ได้แต่constในตระกูลภาษา Cนั้นแตกต่างจากโครงสร้างที่คล้ายกันในภาษาอื่น ๆ ตรงที่เป็นส่วนหนึ่งของประเภทและด้วยเหตุนี้จึงมีพฤติกรรมที่ซับซ้อนเมื่อรวมกับพอยเตอร์การอ้างอิงประเภทข้อมูลแบบผสมและการตรวจสอบประเภทในภาษาอื่น ๆ ข้อมูลไม่ได้อยู่ในตำแหน่งหน่วยความจำ เดียว แต่จะถูกคัดลอกในเวลา คอมไพล์สำหรับการใช้งานแต่ละครั้ง[ 1 ]ภาษาที่ใช้ ได้แก่C , C++ , D , JavaScript , JuliaและRust

การแนะนำ

เมื่อนำไปใช้ในการประกาศอ็อบเจ็กต์ [ a ]จะบ่งชี้ว่าอ็อบเจ็กต์นั้นเป็นค่าคงที่ : ค่า ของมัน ไม่สามารถเปลี่ยนแปลงได้ ต่างจากตัวแปร การใช้งานพื้นฐานนี้ – เพื่อประกาศค่าคงที่ – มีความคล้ายคลึงกันในภาษาโปรแกรมอื่นๆ อีกมากมาย

อย่างไรก็ตาม ในภาษาตระกูล C นั้น ตัวแปร `x` constเป็นส่วนหนึ่งของชนิดข้อมูลไม่ใช่ส่วนหนึ่งของวัตถุ ซึ่งแตกต่างจากภาษาอื่นๆ ตัวอย่างเช่น ในภาษา C `x` ประกาศวัตถุชนิดหนึ่งที่มีชนิดข้อมูล `x` – ตัวแปร `x` เป็นส่วนหนึ่งของชนิดข้อมูล เหมือนกับการแยกวิเคราะห์เป็น "(const int) x" – ในขณะที่ในภาษา Ada ` x` ประกาศค่าคงที่ (ซึ่งเป็นวัตถุชนิดหนึ่ง) ที่มีชนิดข้อมูล `x`: ตัวแปร `x` เป็นส่วนหนึ่งของวัตถุแต่ไม่ใช่ส่วนหนึ่งของชนิดข้อมูล constintx=1;xconst intconstX:constantINTEGER:=1_XINTEGERconstant

สิ่งนี้มีผลลัพธ์ที่ละเอียดอ่อนสองประการ ประการแรกconstสามารถนำไปใช้กับส่วนต่างๆ ของประเภทที่ซับซ้อนกว่าได้ – ตัวอย่างเช่นconst int* const x;การประกาศตัวชี้คงที่ไปยังจำนวนเต็มคงที่ ในขณะที่const int* x;การประกาศตัวชี้ตัวแปรไปยังจำนวนเต็มคงที่ และint* const x;การประกาศตัวชี้คงที่ไปยังจำนวนเต็มตัวแปร ประการที่สอง เนื่องจากconstเป็นส่วนหนึ่งของประเภท จึงต้องตรงกันในส่วนของการตรวจสอบประเภท ตัวอย่างเช่น โค้ดต่อไปนี้ไม่ถูกต้อง:

void f ( int & x ); // ... const int i ; f ( i );

เนื่องจากอาร์กิวเมนต์ของfต้องเป็น จำนวนเต็มที่ เปลี่ยนแปลงได้แต่iเป็น จำนวนเต็ม คงที่การจับคู่แบบนี้เป็นรูปแบบหนึ่งของความถูกต้องของโปรแกรมและเรียกว่าconst-correctnessซึ่งช่วยให้เกิดรูปแบบการเขียนโปรแกรมโดยสัญญา โดยที่ฟังก์ชันระบุเป็นส่วนหนึ่งของลายเซ็นประเภทว่าฟังก์ชันนั้นแก้ไขอาร์กิวเมนต์หรือไม่ และค่าส่งคืนสามารถแก้ไขได้หรือไม่ การตรวจสอบประเภทนี้ส่วนใหญ่เกี่ยวข้องกับพอยเตอร์และตัวอ้างอิง ไม่ใช่ประเภทค่าพื้นฐานเช่นจำนวนเต็ม แต่ยังรวมถึงประเภทข้อมูลแบบผสมหรือประเภทแม่แบบ เช่นคอนเทนเนอร์มันถูกซ่อนไว้โดยข้อเท็จจริงที่ว่าconstมักจะสามารถละเว้นได้ เนื่องจากการแปลงประเภท ( การแปลงประเภท โดยปริยาย ) และภาษา C เป็นแบบเรียกโดยค่า (C++ และ D เป็นแบบเรียกโดยค่าหรือเรียกโดยอ้างอิง)

ผลที่ตามมา

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

ความแตกต่างจากค่าคงที่

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

การใช้งานอื่นๆ

นอกจากนี้ ฟังก์ชันสมาชิก (ที่ไม่ใช่แบบคงที่) สามารถประกาศเป็น `const` ได้constในกรณีนี้thisตัวชี้ภายในฟังก์ชันดังกล่าวจะเป็นประเภท `const` const T*แทนที่จะเป็นเพียงประเภท `const` T*[ 2 ] ซึ่งหมายความว่าฟังก์ชันที่ไม่ใช่ `const` สำหรับวัตถุนี้ไม่สามารถเรียกจากภายในฟังก์ชันดังกล่าวได้ และตัวแปรสมาชิก ก็ไม่สามารถ แก้ไขได้ ใน C++ ตัวแปรสมาชิกสามารถประกาศเป็น `const` ได้mutableซึ่งบ่งชี้ว่าข้อจำกัดนี้ไม่ใช้กับตัวแปรนั้น ในบางกรณี สิ่งนี้อาจมีประโยชน์ เช่น ในการแคชการนับการอ้างอิงและการซิงโครไนซ์ข้อมูลในกรณีเหล่านี้ ความหมายเชิงตรรกะ (สถานะ) ของวัตถุจะไม่เปลี่ยนแปลง แต่วัตถุนั้นไม่คงที่ทางกายภาพ เนื่องจากการแสดงผลแบบบิตอาจเปลี่ยนแปลง ได้

ไวยากรณ์

ในภาษา C, C++ และ D ประเภทข้อมูลทั้งหมด รวมถึงประเภทข้อมูลที่ผู้ใช้กำหนด สามารถประกาศได้constและความถูกต้องของ const กำหนดให้ตัวแปรหรือวัตถุทั้งหมดควรได้รับการประกาศเช่นนั้น เว้นแต่จะต้องแก้ไข การใช้ const อย่างมีประสิทธิภาพเช่นนี้constทำให้ค่าต่างๆ "เข้าใจ ติดตาม และวิเคราะห์ได้ง่ายขึ้น" [ 3 ]และด้วยเหตุนี้จึงเพิ่มความสามารถในการอ่านและความเข้าใจของโค้ด และทำให้การทำงานเป็นทีมและการบำรุงรักษาโค้ดง่ายขึ้น เนื่องจากมีการสื่อสารข้อมูลเกี่ยวกับการใช้งานที่ตั้งใจไว้ของค่า ซึ่งสามารถช่วย ทั้ง คอมไพเลอร์และนักพัฒนาในการวิเคราะห์โค้ด นอกจากนี้ยังช่วยให้คอมไพเลอร์ที่ปรับแต่งสามารถสร้างโค้ดที่มีประสิทธิภาพมากขึ้นได้[ 4 ]

ประเภทข้อมูลพื้นฐาน

สำหรับชนิดข้อมูลที่ไม่ใช่ตัวชี้ การใช้constตัวระบุคุณสมบัติทำได้ง่าย สามารถวางไว้ด้านใดด้านหนึ่งของชนิดข้อมูลบางประเภทได้ด้วยเหตุผลทางประวัติศาสตร์ (ตัวอย่างเช่นconst char foo = 'a';เทียบเท่ากับchar const foo = 'a';) ในบางการใช้งาน การใช้constสองครั้ง (ตัวอย่างเช่นconst char constหรือchar const const) จะทำให้เกิดคำเตือนแต่ไม่ใช่ข้อผิดพลาด

คำแนะนำและข้อมูลอ้างอิง

สำหรับประเภทพอยน์เตอร์และรีเฟอเรนซ์ ความหมายของ `null` constนั้นซับซ้อนกว่า – ทั้งตัวพอยน์เตอร์เอง ค่าที่ถูกชี้ไป หรือทั้งสองอย่าง สามารถเป็น `null` constได้ นอกจากนี้ ไวยากรณ์ยังอาจทำให้สับสนได้ พอยน์เตอร์สามารถประกาศเป็นconstพอยน์เตอร์ไปยังค่าที่เขียนได้ หรือพอยน์เตอร์ที่เขียนได้ไปยังconstค่า หรือconstพอยน์เตอร์ไปยังconstค่าได้ตัวconstชี้ไม่สามารถกำหนดค่าใหม่ให้ชี้ไปยังวัตถุอื่นที่ไม่ใช่วัตถุที่กำหนดค่าไว้ในตอนแรกได้ แต่สามารถใช้เพื่อแก้ไขค่าที่ชี้ไป (เรียกว่าตัวถูกชี้ ) ได้[ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ]ตัวแปรอ้างอิงใน C++ เป็นไวยากรณ์ทางเลือกสำหรับconstตัวชี้ ในทางกลับกัน ตัวชี้ไปconstยังวัตถุสามารถกำหนดค่าใหม่ให้ชี้ไปยังตำแหน่งหน่วยความจำอื่นได้ (ซึ่งควรเป็นวัตถุประเภทเดียวกันหรือประเภทที่แปลงได้) แต่ไม่สามารถใช้เพื่อแก้ไขหน่วยความจำที่ชี้ไป ตัวชี้ไปยังconstวัตถุconstยังสามารถประกาศได้ และไม่สามารถใช้เพื่อแก้ไขตัวถูกชี้หรือกำหนดค่าใหม่ให้ชี้ไปยังวัตถุอื่นได้ โค้ดต่อไปนี้แสดงให้เห็นถึงความแตกต่างเหล่านี้:

void foo ( int * p , const int * cp , int * const pc , const int * const cpc ) { * p = 0 ; // ถูกต้อง: แก้ไขข้อมูลที่ชี้โดยตัวชี้p = NULL ; // ถูกต้อง: แก้ไขตัวชี้* cp = 0 ; // เกิดข้อผิดพลาด! ไม่สามารถแก้ไขข้อมูลที่ชี้โดยตัวชี้ได้cp = NULL ; // ถูกต้อง: แก้ไขตัวชี้ได้* pc = 0 ; // ถูกต้อง: แก้ไขข้อมูลที่ชี้โดยตัวชี้pc = NULL ; // ผิดพลาด! ไม่สามารถแก้ไขตัวชี้ได้* cpc = 0 ; // ข้อผิดพลาด! ไม่สามารถแก้ไขข้อมูลที่ชี้ไปcpc = NULL ; // ข้อผิดพลาด! ไม่สามารถแก้ไขตัวชี้ได้}

อนุสัญญา C

ตามธรรมเนียมการประกาศตัวแปรในภาษา C การประกาศจะอยู่หลังการใช้งาน และค่า*ในตัวชี้จะถูกเขียนไว้บนตัวชี้เพื่อบ่งบอกถึงการเข้าถึงค่าที่ชี้ โดยตัว ชี้ ตัวอย่างเช่น ในการประกาศint *pตัวแปร รูปแบบที่เข้าถึงค่าโดย ตัวชี้ *pคือค่าคงที่intในขณะที่รูปแบบอ้างอิงpคือตัวชี้ไปยังค่าคงที่intดังนั้น ค่าในตัวชี้ constจะแก้ไขชื่อทางด้านขวา ส่วนธรรมเนียมในภาษา C++ จะเชื่อมโยงค่า ในตัวชี้ *กับชนิดข้อมูล เช่น ในint* pตัวแปร และอ่านค่าconstในตัวชี้ว่าเป็นการแก้ไขชนิดข้อมูลทางด้านซ้าย ดังนั้น ค่าในตัว const int * cpชี้จึงสามารถอ่านได้ว่า " *cpคือconst intค่าคงที่" (ค่าคงที่) หรือ " cpคือค่าคงที่const int *" (ตัวชี้เป็นตัวชี้ไปยังจำนวนเต็มคงที่) ดังนั้น:

int * p ; // *p เป็นค่าจำนวนเต็มconst int * cp ; // *cp เป็นค่าคงที่จำนวนเต็มint * const pc ; // pc เป็นค่าคงที่จำนวนเต็มconst int * const cpc ; // cpc เป็นตัวชี้ค่าคงที่และชี้ไปยังค่าคงที่

ธรรมเนียมปฏิบัติของ C++

ตามธรรมเนียมของ C++ ที่วิเคราะห์ชนิดข้อมูล ไม่ใช่ค่าข้อมูลกฎทั่วไปคือการอ่านการประกาศจากขวาไปซ้าย ดังนั้น ทุกอย่างที่อยู่ทางซ้ายของเครื่องหมายดอกจัน (*) สามารถระบุได้ว่าเป็นชนิดข้อมูลที่ชี้ไป และทุกอย่างที่อยู่ทางขวาของเครื่องหมายดอกจันคือคุณสมบัติของตัวชี้ ตัวอย่างเช่น ในตัวอย่างข้างต้นconst int*สามารถอ่านได้ว่าเป็นตัวชี้ที่เขียนได้ซึ่งชี้ไปยังจำนวนเต็มที่เขียนไม่ได้ และint* constสามารถอ่านได้ว่าเป็นตัวชี้ที่เขียนไม่ได้ซึ่งชี้ไปยังจำนวนเต็มที่เขียนได้

กฎทั่วไปที่ช่วยให้เข้าใจคำประกาศและคำจำกัดความที่ซับซ้อนได้ง่ายขึ้นมีดังนี้:

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

นี่คือตัวอย่าง:

ส่วนหนึ่งของการแสดงออก
ดับเบิล( ** const ( * fun ( int ))( double ))[ 10 ]
ความหมาย(อ่านจากบนลงล่าง)
ตัวระบุ
สนุก
funคือ...
อ่านไปทางขวา
( int ))
ฟังก์ชันที่คาดหวังint...
ค้นหาการจับคู่ (
( *
ส่งกลับตัวชี้ไปยัง ...
ไปต่อทางขวา
( สองเท่า))
ฟังก์ชันที่คาดหวังdouble...
ค้นหาการจับคู่ (
( ** คงที่
ส่งคืนค่าคงที่ของตัวชี้ไปยังตัวชี้ไปยัง...
ไปต่อทางขวา
[ 10 ]
บล็อกละ 10 ...
อ่านไปทางซ้าย
สองเท่า
doubleส.

เมื่ออ่านจากซ้ายไปขวา สิ่งสำคัญคือต้องอ่านองค์ประกอบจากขวาไปซ้าย ดังนั้น an const int*จึงกลายเป็นตัวชี้ไปยัง aconst intและไม่ใช่constตัวชี้ไปยังint an

ในบางกรณี ภาษา C/C++ อนุญาตconstให้วางคีย์เวิร์ดไว้ทางด้านขวาของชนิดข้อมูลได้ ตัวอย่างบางส่วนมีดังนี้:

const int * cp ; // เทียบเท่ากับ int const* cp, const int * const cpc ; // เทียบเท่ากับ int const* const cpc

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

int ** pp ; // พอยเตอร์ไปยังพอยเตอร์ไปยังจำนวนเต็มconst int ** cpp ; // พอยเตอร์ไปยังพอยเตอร์ไปยังค่าจำนวนเต็มคงที่ (ไม่ใช่พอยเตอร์ไปยังพอยเตอร์คงที่ไปยังจำนวนเต็ม) int * const * pcp ; // พอยเตอร์ไปยังพอยเตอร์คงที่ไปยังค่าจำนวนเต็ม (ไม่ใช่พอยเตอร์คงที่ไปยังพอยเตอร์ไปยังจำนวนเต็ม) int ** const ppc ; // พอยเตอร์คงที่ไปยังพอยเตอร์ไปยังจำนวนเต็ม (ppc ซึ่งเป็นตัวระบุ เป็น const ซึ่งไม่มีความหมาย) const int ** const cppc ; // พอยเตอร์คงที่ไปยังพอยเตอร์ไปยังค่าจำนวนเต็มคงที่

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

// สองวิธี: int * a ; // เครื่องหมายดอกจันจัดชิดซ้ายint * a ; // เครื่องหมายดอกจันจัดชิดขวาint * a , b ; // อาจทำให้สับสน (a เป็นตัวชี้ไปยังจำนวนเต็ม แต่ b เป็นเพียงจำนวนเต็ม) int * a , b ; // a เป็นตัวชี้ไปยังจำนวนเต็ม และ b เป็นจำนวนเต็มint * a , * b ; // วิธีเขียนที่ดูไม่สวยงาม (a และ b ต่างก็เป็นตัวชี้ไปยังจำนวนเต็ม แต่การเขียนแบบนี้ดูยุ่งยาก) int * a , * b ;

โปรดทราบว่าในC#การจัดตำแหน่งเครื่องหมายดอกจันชิดขวาถือว่าผิดกฎหมาย[ 10 ]

int * a , * b ; // ไม่ถูกต้องใน C# int * a , b ; // ประกาศตัวแปร int* สองตัว

คำถามที่พบบ่อยของ Bjarne Stroustrup แนะนำให้ประกาศตัวแปรเพียงตัวเดียวต่อบรรทัดหากใช้รูปแบบ C++ เพื่อหลีกเลี่ยงปัญหานี้[ 11 ]

หลักการเดียวกันนี้ใช้ได้กับการกำหนดค่าอ้างอิงและค่าอ้างอิง rvalue ด้วยเช่นกัน:

int i = 22 ; const int & cr = i ; const int & cr2 = i , cr3 = i ; // สับสน: // cr2 เป็น reference แต่ cr3 ไม่ใช่: // cr3 เป็นค่าคงที่ int ที่เริ่มต้นด้วยค่าของ i// ข้อผิดพลาด: เนื่องจากการอ้างอิงไม่สามารถเปลี่ยนแปลงได้อยู่แล้วint & const rc = myInt ;// C++: int && rref = int ( 5 ), value = 10 ; // สับสน: rref เป็นการอ้างอิง rvalue แต่ value เป็นเพียง int ธรรมดาint && rref = int ( 5 ), value = 10 ;

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

พารามิเตอร์และตัวแปร

constตัวแปรสามารถประกาศได้ทั้งในพารามิเตอร์ของฟังก์ชันและในตัวแปร ( แบบสแตติกหรือแบบอัตโนมัติ รวมถึงตัวแปรส่วนกลางหรือตัวแปรส่วน) การตีความจะแตกต่างกันไปตามการใช้งานconstตัวแปรสแตติก ( ตัวแปรส่วนกลางหรือตัวแปรส่วนท้องถิ่นแบบ สแตติก ) คือค่าคงที่ และอาจใช้สำหรับข้อมูล เช่น ค่าคงที่ทางคณิตศาสตร์ เช่น ซึ่งconst double PI = 3.14159ในความเป็นจริงอาจยาวกว่า หรือพารามิเตอร์โดยรวมในขั้นตอนการคอมไพล์constตัวแปรอัตโนมัติ (ตัวแปรส่วนท้องถิ่นที่ไม่ใช่สแตติก) หมายความว่า มี การกำหนดค่าเพียงครั้งเดียวแม้ว่าอาจใช้ค่าที่แตกต่างกันในแต่ละครั้ง เช่นconst int x_squared = x * xพารามิเตอร์constในการส่งผ่านแบบอ้างอิงหมายความว่าค่าที่อ้างอิงจะไม่ถูกแก้ไข – มันเป็นส่วนหนึ่งของข้อตกลง – ในขณะที่constพารามิเตอร์ในการส่งผ่านแบบค่า (หรือตัวชี้เอง ในการส่งผ่านแบบอ้างอิง) ไม่ได้เพิ่มอะไรลงในอินเทอร์เฟซ (เนื่องจากค่าถูกคัดลอกแล้ว) แต่บ่งชี้ว่าภายใน ฟังก์ชันไม่ได้แก้ไขสำเนาส่วนท้องถิ่นของพารามิเตอร์ (เป็นการกำหนดค่าเพียงครั้งเดียว) ด้วยเหตุนี้ บางคนจึงนิยมใช้constในพารามิเตอร์เฉพาะสำหรับการส่งผ่านแบบอ้างอิงเท่านั้น ซึ่งจะเปลี่ยนแปลงข้อตกลง แต่ไม่ใช้สำหรับการส่งผ่านแบบค่า ซึ่งจะเปิดเผยการใช้งานภายใน

ซี++

ในภาษา C++ มีสี่ประเภทที่แตกต่างกันดังนี้const:

  • constค่าคงที่รันไทม์แบบดั้งเดิม
  • constexprค่าคงที่หรือนิพจน์ที่สามารถประเมินค่าได้ในระหว่างการคอมไพล์ (ดูconstexpr )
  • constevalซึ่งประกาศว่าการเรียกใช้ฟังก์ชันใดๆ จะต้องให้ค่าที่เป็นนิพจน์ที่กำหนดในขั้นตอนการคอมไพล์
  • constinitซึ่งประกาศว่านิพจน์นั้นมีการกำหนดค่าเริ่มต้นแบบคงที่ (ค่าคงที่)

วิธีการ

เพื่อให้สามารถใช้ประโยชน์จาก แนวทาง การออกแบบโดยสัญญา (design by contract ) สำหรับประเภทที่ผู้ใช้กำหนดเอง (struct และ classes) ซึ่งสามารถมีเมธอดและข้อมูลสมาชิกได้ โปรแกรมเมอร์อาจติดแท็กเมธอดอินสแตนซ์ราวกับว่าconstเมธอดเหล่านั้นไม่ได้แก้ไขสมาชิกข้อมูลของอ็อบเจ็กต์ การใช้constตัวกำหนดคุณสมบัติ (qualifier) ​​กับเมธอดอินสแตนซ์จึงเป็นคุณสมบัติที่สำคัญสำหรับความถูกต้องของค่าคงที่ (const-correctness) และไม่มีใน ภาษา เชิงวัตถุ อื่นๆ เช่นJavaและC#หรือในC++/CLIหรือManaged Extensions for C++ของMicrosoftในขณะที่เมธอดสามารถเรียกได้ทั้งจาก อ็อบเจ็กต์ และไม่ใช่อ็อบเจ็กต์ เมธอดที่ไม่ใช่อ็อบเจ็กต์สามารถเรียกได้โดยไม่ใช่อ็อบเจ็กต์ เท่านั้น ตัวแก้ไขบนเมธอดอินสแตนซ์จะใช้กับอ็อบเจ็กต์ที่ชี้โดยตัวชี้ " " ซึ่งเป็นอาร์กิวเมนต์โดยปริยายที่ส่งผ่านไปยังเมธอดอินสแตนซ์ทั้งหมด ดังนั้นการมีเมธอดจึงเป็นวิธีหนึ่งในการใช้ความถูกต้องของค่าคงที่กับอาร์กิวเมนต์ตัวชี้ " " โดยปริยายเช่นเดียวกับอาร์กิวเมนต์อื่นๆ constconstconstconstconstconstthisconstthis

ตัวอย่างนี้แสดงให้เห็นถึง:

คลาสInteger { private : int i ; public : // สังเกตแท็ก const [[ nodiscard ]] int get () const noexcept { return i ; }// โปรดสังเกตว่าไม่มี "const" void set ( int j ) noexcept { i = j ; } };void foo ( Integer & nonConstInteger , const Integer & constInteger ) { int y = nonConstInteger . get (); // ถูกต้องint x = constInteger . get (); // ถูกต้อง: get() เป็น constnonConstInteger.set ( 10 ); // ถูกต้อง: nonConstInteger สามารถแก้ไขได้constInteger.set( 10 ) ; // ผิดพลาด! set ( ) เป็นเมธอด ที่ไม่ใช่ const และ constInteger เป็นอ็อบเจ็กต์ที่มีคุณสมบัติ const }

ในโค้ดข้างต้นthisพอยเตอร์ " " ที่ ระบุโดยปริยาย set()มีประเภท " Integer* const" ในขณะที่thisพอยเตอร์ " " get()มีประเภท " Integer const* const" ซึ่งบ่งชี้ว่าเมธอดไม่สามารถแก้ไขอ็อบเจ็กต์ผ่านthisพอยเตอร์ " " ได้

บ่อยครั้งที่โปรแกรมเมอร์จะใส่ทั้งเมธอดแบบเรียกใช้ฟังก์ชัน (a) constและเมธอดแบบเรียกใช้ฟังก์ชัน (non-a const) ที่มีชื่อเดียวกัน (แต่การใช้งานอาจแตกต่างกันมาก) ไว้ในคลาสเดียวกัน เพื่อรองรับผู้เรียกใช้ทั้งสองประเภท ลองพิจารณาตัวอย่างต่อไปนี้:

import std ;โดยใช้std :: array ;คลาสIntegerArray { private : array < int , 100 > data ; public : [[ nodiscard ]] int & get ( int i ) noexcept { return data [ i ]; }[[ nodiscard ]] int const & get ( int i ) const noexcept { return data [ i ]; } };void foo ( IntegerArray & a , const IntegerArray & ca ) { // รับการอ้างอิงไปยังองค์ประกอบของอาร์เรย์// และแก้ไขค่าที่อ้างอิงa.get ( 5 ) = 42 ; // ถูกต้อง! (เรียกใช้: int& IntegerArray::get(int)) ca.get ( 5 ) = 42 ; // เกิดข้อผิดพลาด! ( เรียกใช้: int const & IntegerArray::get(int) const ) }

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

ช่องโหว่ของความถูกต้องตามรัฐธรรมนูญ

มีช่องโหว่หลายประการที่ทำให้การใช้ const ไม่ถูกต้องสมบูรณ์แบบในภาษา C และ C++ ช่องโหว่เหล่านี้มีอยู่เป็นหลักเพื่อความเข้ากันได้กับโค้ดที่มีอยู่เดิม

วิธีแรก ซึ่งใช้ได้เฉพาะกับภาษา C++ เท่านั้น คือการใช้ `const` const_castซึ่งช่วยให้โปรแกรมเมอร์สามารถลบconstตัวระบุคุณสมบัติ (qualifier) ​​ออก ทำให้วัตถุใดๆ ก็สามารถแก้ไขได้ ความจำเป็นในการลบตัวระบุคุณสมบัติเกิดขึ้นเมื่อใช้โค้ดและไลบรารีที่มีอยู่แล้วซึ่งไม่สามารถแก้ไขได้ แต่ไม่ถูกต้องตามหลัก `const` ตัวอย่างเช่น ลองพิจารณาโค้ดต่อไปนี้:

// ต้นแบบของฟังก์ชันที่เราไม่สามารถเปลี่ยนแปลงได้ แต่เรารู้ว่าฟังก์ชันนี้ไม่ได้แก้ไขค่าที่ชี้โดยตัวชี้ที่ส่งเข้ามาvoid myLibFunc ( int * p , int size );void callLibFunc ( const int * p , int size ) { myLibFunc ( p , size ); // เกิดข้อผิดพลาด! ละทิ้งคุณสมบัติ constint * nonConstPtr = const_cast < int *> ( p ); // ลบตัวระบุmyLibFunc ( nonConstPtr , size ); // ตกลง}

อย่างไรก็ตาม การพยายามแก้ไขอ็อบเจ็กต์ที่ประกาศconstโดยใช้การแปลงประเภทแบบ constจะส่งผลให้เกิดพฤติกรรมที่ไม่แน่นอนตามมาตรฐาน ISO C++ ในตัวอย่างข้างต้น หากptrอ้างอิงถึงตัวแปรส่วนกลาง ตัวแปรภายใน หรือตัวแปรสมาชิกที่ประกาศเป็นconstหรืออ็อบเจ็กต์ที่จัดสรรบนฮีปผ่านnew int constโค้ดจะถูกต้องก็ต่อเมื่อLibraryFuncไม่ได้แก้ไขค่าที่ชี้โดยptrจริงๆ

ภาษา C มีความจำเป็นต้องมีช่องโหว่เนื่องจากมีสถานการณ์บางอย่างเกิดขึ้น ตัวแปรที่มีระยะเวลาการจัดเก็บแบบคงที่ (static storage duration) สามารถกำหนดค่าเริ่มต้นได้ อย่างไรก็ตาม ตัวกำหนดค่าเริ่มต้นสามารถใช้ได้เฉพาะค่าคงที่ เช่น ค่าคงที่สตริงและค่าคงที่อื่นๆ เท่านั้น และไม่อนุญาตให้ใช้องค์ประกอบที่ไม่ใช่ค่าคงที่ เช่น ชื่อตัวแปร ไม่ว่าองค์ประกอบของตัวกำหนดค่าเริ่มต้นจะถูกประกาศconstหรือไม่ หรือไม่ว่าตัวแปรที่มีระยะเวลาการจัดเก็บแบบคงที่จะถูกประกาศconstหรือไม่ก็ตาม มีวิธีที่ไม่สามารถใช้งานได้ในทุกแพลตฟอร์มในการกำหนดค่าเริ่มต้นให้constกับตัวแปรที่มีระยะเวลาการจัดเก็บแบบคงที่ โดยการสร้างการแปลงประเภทอย่างระมัดระวังทางด้านซ้ายของการกำหนดค่าในภายหลังconstตัวแปรสามารถถูกเขียนเข้าไปได้ ซึ่งเป็นการลบconstคุณสมบัตินั้นออกไปและ 'กำหนดค่าเริ่มต้น' ด้วยองค์ประกอบที่ไม่ใช่ค่าคงที่ เช่นconstตัวแปรอื่นๆ เป็นต้น การเขียนลงในconstตัวแปรด้วยวิธีนี้อาจทำงานได้ตามที่ตั้งใจไว้ แต่จะทำให้เกิดพฤติกรรมที่ไม่แน่นอนและขัดแย้งกับความถูกต้องของค่าคงที่อย่างร้ายแรง

constexpr size_t BUFFER_SIZE = 8 * 1024 ; const size_t userTextBufferSize ; // ค่าเริ่มต้นขึ้นอยู่กับ const BUFFER_SIZE ไม่สามารถกำหนดค่าเริ่มต้นได้ที่นี่...int setupUserTextBox ( TextBox * defaultTextBoxType , Rectangle * defaultTextBoxLocation ) { * ( size_t * ) & userTextBufferSize = BUFFER_SIZE - sizeof ( TextBoxControls ); // คำเตือน: อาจใช้งานได้ แต่ไม่รับประกันโดยภาษา C ... }

ช่องโหว่อีกประการหนึ่ง[ 12 ]ใช้ได้กับทั้ง C และ C++ โดยเฉพาะ ภาษาเหล่านี้กำหนดว่าตัวชี้สมาชิกและการอ้างอิงนั้น "ตื้น" เมื่อเทียบกับสถานะconstของเจ้าของ กล่าวคือ วัตถุที่บรรจุซึ่งconstมีสมาชิกทั้งหมดconstยกเว้นสมาชิกที่ชี้ไป (และอ้างอิง) ยังคงเปลี่ยนแปลงได้ เพื่อให้เห็นภาพ ลองพิจารณาโค้ด C++ นี้:

struct MyStruct { int val ; int * ptr ; };void foo ( const MyStruct & s ) { int i = 42 ; s . val = i ; // ข้อผิดพลาด: s เป็น const ดังนั้น val ก็เป็น const int เช่นกันs . ptr = & i ; // ข้อผิดพลาด: s เป็น const ดังนั้น ptr เป็นตัวชี้ const ไปยัง int * s . ptr = i ; // ถูกต้อง: ข้อมูลที่ชี้โดย ptr สามารถเปลี่ยนแปลงได้เสมอ แม้ว่าบางครั้งอาจไม่เป็นที่ต้องการก็ตาม}

แม้ว่าอ็อบเจ็กต์sที่ส่งผ่านเข้าไปfoo()จะเป็นค่าคงที่ ซึ่งทำให้สมาชิกทั้งหมดของมันเป็นค่าคงที่ แต่ตัวชี้ที่เข้าถึงได้ผ่านทางนั้นs.ptrยังคงสามารถแก้ไขได้ แม้ว่านี่อาจไม่เป็นที่พึงปรารถนาจากมุมมองของconstความถูกต้อง เพราะsอาจเป็นของคอนเทนเนอร์แต่เพียงผู้เดียวที่เป็นเจ้าของตัวชี้ ด้วยเหตุนี้ Meyers จึงโต้แย้งว่าค่าเริ่มต้นสำหรับตัวชี้และตัวอ้างอิงสมาชิกควรเป็น "แบบลึก" constซึ่งสามารถแทนที่ได้ด้วยmutableตัวกำหนดคุณสมบัติเมื่อตัวชี้ไม่ได้เป็นของคอนเทนเนอร์ แต่กลยุทธ์นี้จะสร้างปัญหาความเข้ากันได้กับโค้ดที่มีอยู่ ดังนั้นด้วยเหตุผลทางประวัติศาสตร์ ช่องโหว่นี้จึงยังคงเปิดอยู่ใน C และ C++

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

สุดท้ายนี้ ฟังก์ชันหลายฟังก์ชันในไลบรารีมาตรฐาน Cละเมิดความถูกต้องของ const ก่อนC23เนื่องจากฟังก์ชันเหล่านี้รับconstพอยเตอร์ไปยังสตริงอักขระและส่งคืนconstพอยเตอร์ที่ไม่ใช่ const ไปยังส่วนหนึ่งของสตริงเดียวกันstrstrและstrchrฟังก์ชันเหล่านี้ก็อยู่ในกลุ่มนี้ด้วย การใช้งานไลบรารีมาตรฐาน C++ บางอย่าง เช่นของ Microsoft [ 13 ]พยายามปิดช่องโหว่นี้โดยการจัดเตรียมฟังก์ชันบางเวอร์ชันที่มีการโอเวอร์โหลดconst สองเวอร์ชัน ได้แก่ เวอร์ชัน " " และconstเวอร์ชัน "ไม่ใช่ "

ปัญหา

การใช้ระบบชนิดข้อมูลเพื่อแสดงค่าคงที่นำไปสู่ความซับซ้อนและปัญหาต่างๆ มากมาย และด้วยเหตุนี้จึงถูกวิพากษ์วิจารณ์และไม่ได้รับการยอมรับนอกเหนือจากตระกูลภาษา C ที่แคบๆ อย่าง C, C++ และ D Java และ C# ซึ่งได้รับอิทธิพลอย่างมากจาก C และ C++ ต่างก็ปฏิเสธconstตัวกำหนดชนิดข้อมูลแบบ `-style` อย่างชัดเจน โดยเลือกที่จะแสดงค่าคงที่ด้วยคำหลักที่ใช้กับตัวระบุ (`-i` finalใน Java constและreadonly`-i` ใน C#) แม้แต่ภายใน C และ C++ เอง การใช้งาน `-i` constก็แตกต่างกันอย่างมาก โดยบางโครงการและองค์กรใช้มันอย่างสม่ำเสมอ ในขณะที่บางแห่งหลีกเลี่ยงการใช้

strchrปัญหา

ตัวconstกำหนดประเภท (type qualifier) ​​ก่อให้เกิดปัญหาเมื่อตรรกะของฟังก์ชันไม่คำนึงถึงว่าอินพุตเป็นค่าคงที่หรือไม่ แต่ส่งคืนค่าที่ควรมีประเภทที่กำหนดไว้เหมือนกับอินพุต กล่าวคือ สำหรับฟังก์ชันเหล่านี้ หากอินพุตเป็นค่าคงที่ (กำหนดประเภทเป็น const) ค่าที่ส่งคืนก็ควรเป็นค่าคงที่เช่นกัน แต่หากอินพุตเป็นตัวแปร (ไม่ได้constกำหนดประเภทเป็น const) ค่าที่ส่งคืนก็ควรเป็นตัวแปรเช่นกัน เนื่องจากลายเซ็นประเภทของฟังก์ชันเหล่านี้แตกต่างกัน จึงต้องใช้ฟังก์ชันสองฟังก์ชัน (หรืออาจมากกว่านั้น ในกรณีที่มีอินพุตหลายตัว) ที่มีตรรกะเดียวกัน ซึ่งเป็นรูปแบบหนึ่งของการเขียนโปรแกรมแบบเจเนริก (generic programming )

ปัญหานี้เกิดขึ้นแม้กระทั่งกับฟังก์ชันง่ายๆ ในไลบรารีมาตรฐานของ C โดยเฉพาะอย่างยิ่งstrchr; ข้อสังเกตนี้ได้รับการให้เครดิตโดย Ritchie แก่ Tom Plum ในช่วงกลางทศวรรษ 1980 [ 14 ] ฟังก์ชัน นี้strchrค้นหาอักขระในสตริง ในทางรูปแบบ ฟังก์ชันนี้จะส่งคืนตัวชี้ไปยังการเกิดขึ้นครั้งแรกของอักขระcในสตริงsและใน C แบบคลาสสิก (K&R C) ต้นแบบของฟังก์ชันนี้คือ:

char * strchr ( char * s , int c );

ฟังก์ชัน นี้strchrไม่ได้แก้ไขสตริงอินพุต แต่ค่าที่ส่งกลับมักถูกผู้เรียกใช้นำไปใช้แก้ไขสตริง เช่น:

ถ้า( p = strchr ( q , '/' )) { * p = ' ' ; }

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

ในภาษา C++ จะทำได้โดยการใช้การโอเวอร์โหลดฟังก์ชันซึ่งโดยทั่วไปจะใช้เทมเพลตส่งผลให้มีฟังก์ชันสองฟังก์ชัน เพื่อให้ค่าที่ส่งคืนมีconstประเภทข้อมูลที่ระบุตรงกับค่าที่ป้อนเข้ามา:

char * strchr ( char * s , int c ); const char * strchr ( const char * s , int c );

สิ่งเหล่านี้สามารถกำหนดได้โดยใช้แม่แบบ:

template < T > T * strchr ( T * s , int c ) { ... }

ใน D นี้จะจัดการผ่านinoutคำหลัก ซึ่งทำหน้าที่เป็นตัวแทนแทน const, immutable หรือ unqualified (variable) โดยให้ผลลัพธ์ดังนี้: [ 15 ] [ b ]

inout ( char )* strchr ( inout ( char )* s , int c );

อย่างไรก็ตาม ในภาษา C ทั้งสองอย่างนี้เป็นไปไม่ได้[ c ]เนื่องจากภาษา C ไม่มีการโอเวอร์โหลดฟังก์ชัน และแทนที่จะเป็นเช่นนั้น จะจัดการโดยการมีฟังก์ชันเดียวที่อินพุตเป็นค่าคงที่ แต่เอาต์พุตสามารถเขียนได้:

char * strchr ( const char * s , int c );

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

ตั้งแต่C23 เป็นต้น มา ปัญหานี้ได้รับการแก้ไขโดยใช้_Genericฟังก์ชันของภาษา: ตัวระบุของstrchrและฟังก์ชันอื่นๆ ที่ได้รับผลกระทบจากปัญหานี้ได้ถูกเปลี่ยนเป็นมาโครที่ขยายการเรียกไปยังฟังก์ชันที่เหมาะสม ซึ่งจะส่งคืนconstพอยเตอร์หากมีการส่งพอยเตอร์ไปให้ และส่งคืนพอยเตอร์ที่ไม่มีคุณสมบัติหากมีการส่งพอยเตอร์ที่ไม่มีคุณสมบัติไปให้[ 16 ]

ดี

ในภาษาการเขียนโปรแกรม D เวอร์ชัน 2 มีคำหลักสองคำที่เกี่ยวข้องกับ const [ 17 ] คำหลัก นี้immutableหมายถึงข้อมูลที่ไม่สามารถแก้ไขได้ผ่านการอ้างอิงใดๆconstคำหลักนี้หมายถึงมุมมองที่ไม่สามารถเปลี่ยนแปลงได้ของข้อมูลที่เปลี่ยนแปลงได้ แตกต่างจาก C++ constD constและimmutableเป็นแบบ "ลึก" หรือแบบถ่ายทอดได้และสิ่งใดก็ตามที่สามารถเข้าถึงได้ผ่านออบเจ็กต์constหรือ จะเป็น หรือตามลำดับ immutableconstimmutable

ตัวอย่างการใช้ const เทียบกับ immutable ในภาษา D

int [] foo = new int [ 5 ]; // foo เป็นตัวแปรที่เปลี่ยนแปลงได้const int [] bar = foo ; // bar เป็นมุมมองคงที่ของข้อมูลที่เปลี่ยนแปลงได้immutable int [] baz = foo ; // ข้อผิดพลาด: มุมมองทั้งหมดของข้อมูลที่ไม่สามารถเปลี่ยนแปลงได้ต้องเป็นตัวแปรที่ไม่สามารถเปลี่ยนแปลงได้immutable int [] nums = new immutable ( int )[ 5 ]; // ไม่สามารถสร้างการอ้างอิงที่เปลี่ยนแปลงได้ไปยัง nums ได้const int [] constNums = nums ; // ใช้งานได้ immutable สามารถแปลงเป็น const ได้โดยปริยายint [] mutableNums = nums ; // ข้อผิดพลาด: ไม่สามารถสร้างมุมมองที่เปลี่ยนแปลงได้ของข้อมูลที่ไม่เปลี่ยนแปลงได้

ตัวอย่างของ const แบบถ่ายทอดหรือแบบลึกใน D

คลาสLinkedList { int value ; LinkedList next ; }LinkedList ที่ไม่เปลี่ยนแปลงได้ll = new immutable ( LinkedList ); ll.next.value = 5 ; // คอมไพล์ไม่ผ่าน ll.next เป็นชนิด immutable(LinkedList) // ll.next.value เป็นชนิด immutable(int )

ประวัติศาสตร์

constได้รับการแนะนำโดยBjarne StroustrupในC with Classesซึ่งเป็นรุ่นก่อนหน้าของC++ในปี 1981 และเดิมเรียกว่าreadonly[ 18 ] [ 19 ] สำหรับแรงจูงใจ Stroustrup เขียนว่า: [ 19 ]

"มันทำหน้าที่สองอย่าง: อย่างแรกคือเป็นวิธีการกำหนดค่าคงที่เชิงสัญลักษณ์ที่ปฏิบัติตามกฎขอบเขตและประเภท (กล่าวคือ โดยไม่ต้องใช้มาโคร) และอย่างที่สองคือเป็นวิธีทำให้วัตถุในหน่วยความจำไม่สามารถเปลี่ยนแปลงได้"

การใช้งานครั้งแรก ในฐานะทางเลือกที่มีขอบเขตและประเภทแทนมาโคร ได้รับการดำเนินการในลักษณะเดียวกันสำหรับมาโครที่คล้ายฟังก์ชันผ่านทางinlineคำหลัก ตัวชี้คงที่และ* constสัญกรณ์ ได้รับการแนะนำโดยเดนนิส ริทชี และได้รับการนำมาใช้[ 19 ]

constจากนั้นจึงนำมาใช้ใน C เป็นส่วนหนึ่งของการกำหนดมาตรฐาน และปรากฏในC89 (และเวอร์ชันต่อๆ มา) พร้อมกับตัวกำหนดคุณสมบัติประเภทอื่นvolatile[ 20 ] ตัวกำหนดคุณสมบัติเพิ่มเติมnoaliasได้รับการเสนอแนะในการประชุมเดือนธันวาคม พ.ศ. 2530 ของคณะกรรมการ X3J11 แต่ถูกปฏิเสธ เป้าหมายของมันบรรลุผลสำเร็จในที่สุดด้วยrestrictคำหลักในC99ริชชีไม่ได้สนับสนุนการเพิ่มเติมเหล่านี้มากนัก โดยโต้แย้งว่าพวกมัน "ไม่มีน้ำหนักเพียงพอ" แต่ในที่สุดก็ไม่ได้โต้แย้งให้ลบออกจากมาตรฐาน[ 21 ]

ต่อมา D ได้รับการสืบทอดมาconstจาก C++ ซึ่งรู้จักกันในชื่อตัวสร้างประเภท (ไม่ใช่ตัวกำหนดคุณสมบัติประเภท ) และได้เพิ่มตัวสร้างประเภทอีกสองตัวคือimmutableและinoutเพื่อจัดการกรณีการใช้งานที่เกี่ยวข้อง[ d ]

ภาษาอื่นๆ

ภาษาโปรแกรมอื่นๆ ไม่ได้ใช้โครงสร้างค่าคงที่แบบเดียวกับ C/C++ ถึงแม้ว่าโดยทั่วไปจะมีโครงสร้างที่คล้ายคลึงกันและอาจใช้constคีย์เวิร์ด `constancy` ก็ตาม โดยปกติแล้วจะใช้สำหรับค่าคงที่ (อ็อบเจ็กต์ค่าคงที่) เท่านั้น

C#มีconstคีย์เวิร์ด แต่มีความหมายที่แตกต่างและเรียบง่ายกว่ามาก: มันหมายถึงค่าคงที่ที่กำหนดในขั้นตอนการคอมไพล์ และไม่ได้เป็นส่วนหนึ่งของชนิดข้อมูล โดยพื้นฐานแล้วมันเทียบเท่ากับconstexprในภาษา C และ C++

Nimมีconstคีย์เวิร์ดที่คล้ายกับของ C#: โดยจะประกาศค่าคงที่ในเวลาคอมไพล์แทนที่จะเป็นส่วนหนึ่งของประเภท อย่างไรก็ตาม ใน Nim สามารถประกาศค่าคงที่ได้จากนิพจน์ใดๆ ที่สามารถประเมินได้ในเวลาคอมไพล์[ 22 ] ใน C# เฉพาะประเภทในตัวของ C# เท่านั้นที่สามารถประกาศเป็นค่าคงที่ได้constประเภทที่ผู้ใช้กำหนดเอง รวมถึงคลาส โครงสร้าง และอาร์เรย์ ไม่สามารถประกาศconstเป็น ค่าคงที่ได้ [ 23 ]

Rustมีconstคีย์เวิร์ดที่คล้ายกับของ C# ซึ่งประกาศค่าคงที่ ฟังก์ชัน และสัญลักษณ์อื่นๆ ในเวลาคอมไพล์[ 24 ]ดังนั้นจึงเทียบเท่ากับconstexprจาก C และ C++ โดยพื้นฐาน

Javaไม่ได้ใช้const– แต่ใช้แทนfinalซึ่งสามารถนำไปใช้กับการประกาศ "ตัวแปร" ในพื้นที่และใช้กับตัวระบุไม่ใช่ประเภท มีการใช้งานเชิงวัตถุที่แตกต่างกันสำหรับสมาชิกวัตถุ ซึ่งเป็นที่มาของชื่อ ข้อกำหนดภาษา Java ถือว่าconstเป็นคำสงวน – กล่าวคือ คำที่ไม่สามารถใช้เป็นตัวระบุตัวแปรได้ – แต่ไม่ได้กำหนดความหมายใดๆ ให้กับมัน: มันเป็นคำสงวน (ไม่สามารถใช้ในตัวระบุได้) แต่ไม่ใช่คำหลัก (ไม่มีความหมายพิเศษ) คำหลัก ถูกรวมไว้เพื่อให้คอมไพเลอร์ Java ตรวจจับและเตือนเกี่ยวกับการใช้คำหลัก C++ ที่ไม่ถูกต้อง[ 25 ]มีตั๋วคำขอปรับปรุงสำหรับการใช้งานconstที่ถูกต้องอยู่ในJava Community Processแต่ถูกปิดในปี 2548 เนื่องจากเป็นไปไม่ได้ที่จะใช้งานในลักษณะที่เข้ากันได้กับเวอร์ชันก่อนหน้า[ 26 ]

Ada 83ในยุคปัจจุบันมีแนวคิดเรื่องวัตถุคงที่และconstantคำหลัก โดยอิสระ [ 27 ] [ e ]โดยที่พารามิเตอร์อินพุตและพารามิเตอร์ลูปเป็นค่าคงที่โดยปริยาย ในที่นี้constantเป็นคุณสมบัติของวัตถุ ไม่ใช่ของประเภท

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

ดูเพิ่มเติม

หมายเหตุ

  1. ^ตามหลักการแล้ว เมื่อconstเป็นส่วนหนึ่งของประเภทที่ได้มาภายนอกสุดในการประกาศ ตัวชี้จะทำให้การอธิบายซับซ้อนขึ้น
  2. ^โค้ด D ที่ถูกต้องจะใช้อาร์เรย์แทนตัวชี้ที่นี่ [ 15 ]
  3. ^ใน C11 และเวอร์ชันต่อมา_Genericสามารถใช้เพื่อใช้งาน const-correctstrchrได้
    char * strchr_m ( char * s , int c ); const char * strchr_c ( const char * s , int c ); #define strchr(X,Y) _Generic((X), \  char*: strchr_m, \  const char*: strchr_c \ )(X,Y)
  4. ^ D ยังได้แนะนำsharedตัวสร้างประเภทด้วย แต่สิ่งนี้เกี่ยวข้องกับกรณีการใช้งานของvolatileไม่ใช่const
  5. ^มาตรฐานของภาษา Ada เรียกคำนี้ว่า "คำสงวน " โปรดดูบทความดังกล่าวสำหรับรายละเอียดการใช้งาน
  • "ความถูกต้องตามรัฐธรรมนูญ"โดยเฮิร์บ ซัตเตอร์
  • "การปรับปรุงอย่างต่อเนื่อง?"โดย เฮิร์บ ซัตเตอร์
  • คำถามที่พบบ่อยเกี่ยวกับ C++ ฉบับย่อ: ความถูกต้องของ Constโดย Marshall Cline
  • ส่วน " การแทนที่ค่า " จากหนังสืออิเล็กทรอนิกส์ฟรีเรื่อง Thinking in C++โดยBruce Eckel
  • "Here A Const, There A Const"โดยวอลเตอร์ ไบรท์
  • "ค่าคงที่และค่าไม่เปลี่ยนแปลง" จากข้อกำหนดภาษาโปรแกรม D เวอร์ชัน 2 (ทดลอง)
ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Const_(computer_programming)&oldid=1351269778#Pointee "

สรุปเนื้อหา

ข้อมูลสำคัญจากบทความ

ข้อมูลสำคัญเกี่ยวกับ const (การเขียนโปรแกรมคอมพิวเตอร์)

ใน ภาษาโปรแกรม บาง ภาษา const เป็น ตัวกำหนดประเภท ( คำหลัก ที่ใช้กับ ประเภทข้อมูล ) ที่ระบุว่าข้อมูลนั้นเป็นแบบอ่านอย่างเดียว ในขณะที่สามารถใช้ในการประกาศ ค่าคงที่ได้ แต่ const ใน...

การแนะนำ

เมื่อนำไปใช้ใน การประกาศ อ็อบเจ็กต์ [ a ] จะบ่งชี้ว่าอ็อบเจ็กต์นั้นเป็น ค่าคงที่ : ค่า ของมัน ไม่สามารถเปลี่ยนแปลงได้ ต่างจาก ตัวแปร การใช้งานพื้นฐานนี้ – เพื่อ ประกาศค่าคงที่ – มีความคล้ายคลึงกันในภาษาโปรแกรมอื่นๆ อีกมากมาย

ผลที่ตามมา

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

ความแตกต่างจากค่าคงที่

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