ควรใช้คำหลัก const แบบใดใน C เพื่อหาตัวแปรเมื่อใดและเพื่ออะไร


58

ในขณะที่รับรหัสของฉันได้รับการตรวจสอบที่นี่ปัญหาการใช้constคำหลักเกิดขึ้น ฉันเข้าใจว่ามันใช้สำหรับการใช้งานพฤติกรรมอ่านอย่างเดียวกับตัวแปร

ฉันสับสนเกี่ยวกับสถานการณ์ต่าง ๆ ว่าจะมีประโยชน์เมื่อใด

  • ควรใช้เพื่อความชัดเจนในฟังก์ชั่นต้นแบบหรือไม่
  • ควรใช้เป็นมาตรการรักษาความปลอดภัยระหว่างการพัฒนารหัสหรือไม่
  • มันควรจะใช้ในขอบเขตของฟังก์ชั่นต่าง ๆ เพื่อประกาศค่าคงที่แบบรันไทม์หรือไม่?
  • ควรจะใช้เลยหรือไม่?

คำถามเหล่านี้เป็นเพียงตัวอย่างของความสับสนที่ฉันเผชิญ ความสับสนทั่วไปคือ

  • ควรconstใช้คำหลักในการเขียนโปรแกรม C เมื่อใด
  • สิทธิประโยชน์หลายประเภทที่สามารถได้รับจากการใช้คำหลักนี้ใน C คืออะไร?
  • มีข้อเสียของการใช้constคำหลักหรือไม่


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

ควรใช้คำหลัก const แบบใดใน C เพื่อหาตัวแปรเมื่อใดและเพื่ออะไร

นอกจากนี้ยังสามารถใช้ถ้อยคำใหม่เป็น

การใช้constคำหลักที่เหมาะสมใน C` ด้วยข้อดีข้อเสียของสิ่งเดียวกัน


Specific issues with software developmentสำหรับผู้ที่ลงคะแนนให้มันถูกปิดกรุณาอธิบายว่าทำไมมันไม่ได้ตกอยู่ภายใต้หมวดหมู่ของ ฉันค่อนข้างเฉพาะเจาะจง
Aseem Bansal

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

1
@RobertHarvey คำถามทั้งหมดนี้เป็นเพียงเพื่ออธิบายความสับสนของฉัน คำถามเดียวของฉันยังคงเป็นชื่อของคำถามนี้ คำตอบที่ดีซึ่งครอบคลุมคำถามที่เกี่ยวข้องทั้งหมดเหล่านี้ specific issueดังนั้นจึงยังคงเป็น การใช้constคำหลักที่เหมาะสมใน C` ด้วยข้อดีข้อเสียของสิ่งเดียวกัน
Aseem Bansal

@RobertHarvey ตอนนี้ดีกว่าไหม?
Aseem Bansal

คำตอบ:


64

เมื่อตรวจสอบรหัสฉันใช้กฎต่อไปนี้:

  • ใช้constสำหรับพารามิเตอร์ฟังก์ชันที่ส่งผ่านโดยการอ้างอิงทุกครั้งที่ฟังก์ชันไม่แก้ไข (หรือฟรี) ข้อมูลที่ชี้ไป

    int find(const int *data, size_t size, int value);
  • ใช้constสำหรับค่าคงที่ที่อาจถูกกำหนดโดยใช้ #define หรือ enum เสมอ คอมไพเลอร์สามารถค้นหาข้อมูลในหน่วยความจำแบบอ่านอย่างเดียว (ROM) เป็นผลลัพธ์ (แม้ว่า linker มักจะเป็นเครื่องมือที่ดีกว่าสำหรับจุดประสงค์นี้ในระบบฝังตัว)

    const double PI = 3.14;
  • ไม่เคยใช้ const ในฟังก์ชั่นต้นแบบสำหรับพารามิเตอร์ส่งผ่านโดย ค่า มันไม่มีความหมายและเป็นเพียง 'เสียง'

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, const int value); 
  • ตามความเหมาะสมให้ใช้const volatileกับสถานที่ที่โปรแกรมไม่สามารถเปลี่ยนแปลงได้ แต่อาจยังมีการเปลี่ยนแปลง การลงทะเบียนฮาร์ดแวร์เป็นกรณีการใช้งานทั่วไปที่นี่ตัวอย่างเช่นการลงทะเบียนสถานะที่สะท้อนถึงสถานะอุปกรณ์:

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;

การใช้งานอื่น ๆ เป็นทางเลือก ตัวอย่างเช่นพารามิเตอร์ของฟังก์ชั่นภายในการใช้งานฟังก์ชั่นสามารถทำเครื่องหมายเป็น const

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

หรือฟังก์ชั่นคืนค่าหรือการคำนวณที่ได้รับและไม่เคยเปลี่ยนแปลง:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

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


constปัญหาเกี่ยวกับ คุณอาจได้ยินคำว่า "พิษพิษ" สิ่งนี้เกิดขึ้นเมื่อการเพิ่มconstพารามิเตอร์ฟังก์ชั่นทำให้ 'constness' เผยแพร่

แก้ไข - พิษ const: ตัวอย่างเช่นในฟังก์ชั่น:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

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

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

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchrส่งกลับไม่ได้เป็นchar* const char*เป็นพารามิเตอร์โทรของมันคือ constมันจะต้องโยนchar*พารามิเตอร์การเรียกร้องให้ และในกรณีนี้มันได้ทิ้งคุณสมบัติการจัดเก็บข้อมูลแบบอ่านอย่างเดียวออกไป แก้ไข: - ใช้กับ vars โดยทั่วไปในหน่วยความจำแบบอ่านอย่างเดียว โดย 'ROM' ฉันหมายถึงไม่ใช่แค่ ROM จริง แต่เป็นหน่วยความจำที่มีการป้องกันการเขียนเช่นเดียวกับที่เกิดขึ้นกับส่วนรหัสของโปรแกรมที่ทำงานบนระบบปฏิบัติการทั่วไป

ฟังก์ชั่นไลบรารีมาตรฐานหลายตัวทำงานในลักษณะเดียวกันดังนั้นจงระวัง: เมื่อคุณมีค่าคงที่จริง (เช่นเก็บไว้ใน ROM) คุณจะต้องระมัดระวังไม่ให้สูญเสียความมั่นคง


มันไม่มีความหมายสองอย่างจริงๆ อย่างที่คุณพูดว่า " ฉันจะไม่เปลี่ยนแปลงสิ่งนี้" ตัวอย่างเช่นมันใช้ได้อย่างสมบูรณ์แบบที่จะมีconst volatileตัวแปร
Detly

2
นั่นเป็นความจริงของพารามิเตอร์ฟังก์ชัน แต่สำหรับค่าคงที่ที่ขอบเขตไฟล์ตัวอย่างเช่นconstตัวแปรเป็นแบบอ่านอย่างเดียว: constพูดว่า " ไม่สามารถแก้ไขได้" const volatileในมืออื่น ๆ กล่าวว่า "นี่ไม่สามารถได้รับการแก้ไข แต่ก็อาจมีการเปลี่ยนแปลง" ฉันจะเพิ่มการกล่าวถึง volatiles ให้กับคำตอบของฉัน
William Morris

ฉันไม่เห็นด้วยกับความคิดเห็นของคุณเพียงแค่ความคิดที่ว่ามันเป็นคำสั่งของคอมไพเลอร์ที่จะใส่ไว้ใน ROM นั่นหมายถึงประเภทของ runtime ยามกับพฤติกรรมที่ไม่ได้กำหนด (เช่นการแก้ไข const ผ่านอย่างใด) ซึ่งสามารถนำไปสู่การเข้าใจผิดเช่น "ทำไมฉันสามารถเปลี่ยนconstตัวแปรนี้ผ่าน non- constpointer" (คำถามทั่วไปเกี่ยวกับ SO และฟอรั่ม C อื่น ๆ !) โดยรวมแล้วฉันชอบคำตอบนี้ :)
detly

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

นี่คือภาพรวมที่ดี ผมชอบที่จะวลีที่constประกาศทั้งอ่านอย่างเดียวข้อมูลและอ่านอย่างเดียวมุมมองของข้อมูลที่แตกต่างเป็น“นี้ไม่สามารถแก้ไขได้” กับ“คุณไม่สามารถแก้ไขเรื่องนี้”
Jon Purdy

8

โดยทั่วไปในภาษาการเขียนโปรแกรมใด ๆ แนะนำให้ใช้constหรือปรับปรุงเทียบเท่าตั้งแต่

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

1

ใช่มันเป็นคำตอบของ TheLQ

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

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

int foo(Structure s);

ไปยัง

int foo(const Structure * s);

และไม่ได้รับค่าใช้จ่ายการคัดลอก

เพียงเพิ่มโปรดทราบว่า C มีกฎเฉพาะกับตัวระบุ const ตัวอย่างเช่น,

int b = 1;
const int * a = &b;

ไม่เหมือนกัน

int b = 1;
int * const a = &b;

รหัสแรกจะไม่อนุญาตให้คุณแก้ไข ในกรณีที่สองตัวชี้คงที่ แต่เนื้อหาไม่ได้ดังนั้นคอมไพเลอร์จะอนุญาตให้คุณพูด* a = 3;โดยไม่มีข้อผิดพลาดของคอมไพเลอร์ แต่คุณไม่aสามารถอ้างอิงถึงสิ่งอื่นได้


0

ตามข้อตกลงของ TheLQ:

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

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