const char * const กับ const char *?


110

ฉันกำลังใช้โปรแกรมตัวอย่างเพื่อทำความคุ้นเคยกับ C ++ อีกครั้งและฉันได้พบกับคำถามต่อไปนี้ ขั้นแรกนี่คือรหัสตัวอย่าง:

void print_string(const char * the_string)
{
    cout << the_string << endl;
}

int main () {
    print_string("What's up?");
}

ในโค้ดข้างต้นพารามิเตอร์ที่จะ print_string const char * const the_stringจะได้รับแทน แบบไหนจะถูกต้องกว่ากัน?

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

คำตอบ:


244

ป้องกันคุณจากการหลังการปรับเปลี่ยนภายในthe_string print_stringที่นี่จะเหมาะสมจริง ๆ แต่บางทีการใช้คำฟุ่มเฟือยอาจทำให้นักพัฒนาซอฟต์แวร์ไม่สามารถทำงานได้

char* the_string: ฉันสามารถเปลี่ยนcharที่the_stringจุดและผมสามารถปรับเปลี่ยนcharที่มันชี้

const char* the_string: ฉันสามารถเปลี่ยนcharที่the_stringจุด แต่ฉันไม่สามารถปรับเปลี่ยนcharที่มันชี้

char* const the_string: ฉันไม่สามารถเปลี่ยนcharที่the_stringจุด แต่ผมสามารถปรับเปลี่ยนcharที่มันชี้

const char* const the_string: ฉันไม่สามารถเปลี่ยนcharที่the_stringจุดหรือผมสามารถปรับเปลี่ยนcharที่มันชี้


11
+1 สำหรับประโยคสุดท้าย ความถูกต้องของค่าคงที่เป็นแบบละเอียด แต่ก็คุ้มค่า
mskfisher

6
@Xeo: แบบฟอร์มของคุณสับสนมากยิ่งขึ้นเพราะเป็นการเปลี่ยนตำแหน่งเพียงครั้งเดียวจากการเปลี่ยนความหมายโดยสิ้นเชิง const char *ดีกว่ามากเพราะconstอยู่ฝั่งตรงข้ามที่สมบูรณ์
R .. GitHub STOP HELPING ICE

7
@R .. : อย่างน้อยสำหรับฉันมันก็ไม่ใช่ เมื่ออ่านจากขวาไปซ้ายฉันจะได้ "ตัวชี้ไปยัง const char" สำหรับฉันมันก็รู้สึกดีขึ้นอย่างนั้น
Xeo

6
คุณกำลังหลอกตัวเองเพราะประเภท C อ่านจากภายในสู่ภายนอกไม่ใช่จากซ้ายไปขวา :-)
R .. GitHub STOP HELPING ICE

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

138
  1. ตัวชี้ที่ไม่แน่นอนไปยังอักขระที่ไม่แน่นอน

    char *p;
  2. ตัวชี้ที่ไม่แน่นอนเป็นอักขระคงที่

    const char *p;
  3. ตัวชี้คงที่ไปยังอักขระที่ไม่แน่นอน

    char * const p; 
  4. ตัวชี้คงที่เป็นอักขระคงที่

    const char * const p;

สิ่งนี้ไม่ควร: const char* p; --> constant pointer to mutable characterและ char *const p; --> mutable pointer to constant character
PnotNP

2
@NulledPointer หมายเลขการประกาศ C ++ จะสร้างจากขวาไปซ้าย คุณสามารถอ่านได้const char * pว่า: "p คือตัวชี้ไปยังค่าคงที่ของอักขระ" หรือตัวชี้ที่ไม่สามารถเปลี่ยนแปลงได้สำหรับอักขระคงที่ตามที่ James ระบุอย่างถูกต้อง เช่นเดียวกันกับครั้งที่สอง :)
Samidamaru

28

const char * constหมายถึงตัวชี้และข้อมูลที่ตัวชี้ชี้ไปนั้นมีทั้ง const!

const char *หมายถึงเฉพาะข้อมูลที่ตัวชี้ชี้ไปคือ const ตัวชี้เอง แต่ไม่ใช่ const

ตัวอย่าง.

const char *p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //okay, changing the non-const pointer. 

const char * const p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //error, changing the const pointer. 

20

(ฉันรู้ว่ามันเก่า แต่ฉันก็อยากจะแชร์อยู่ดี)

แค่อยากอธิบายรายละเอียดเกี่ยวกับคำตอบของ Thomas Matthews การประกาศประเภท C ของกฎขวา - ซ้ายค่อนข้างบอกว่า: เมื่ออ่านการประกาศประเภท C เริ่มต้นที่ตัวระบุและไปทางขวาเมื่อคุณทำได้และไปทางซ้ายเมื่อคุณทำไม่ได้

สิ่งนี้อธิบายได้ดีที่สุดด้วยตัวอย่างสองสามตัวอย่าง:

ตัวอย่าง 1

  • เริ่มที่ตัวระบุเราไปทางขวาไม่ได้ก็เลยไปทางซ้าย

    const char* const foo
                ^^^^^

    foo คือค่าคงที่ ...

  • ไปทางซ้าย

    const char* const foo
              ^

    foo เป็นตัวชี้ค่าคงที่เพื่อ ...

  • ไปทางซ้าย

    const char* const foo
          ^^^^

    foo เป็นตัวชี้ค่าคงที่ของถ่าน ...

  • ไปทางซ้าย

    const char* const foo
    ^^^^^

    foo เป็นตัวชี้อย่างต่อเนื่องเพื่อถ่านคงที่ (Complete!)

ตัวอย่าง 2

  • เริ่มที่ตัวระบุเราไปทางขวาไม่ได้ก็เลยไปทางซ้าย

    char* const foo
          ^^^^^

    foo คือค่าคงที่ ...

  • ไปทางซ้าย

    char* const foo
        ^

    foo เป็นตัวชี้ค่าคงที่เพื่อ ...

  • ไปทางซ้าย

    char* const foo
    ^^^^

    foo เป็นตัวชี้ค่าคงที่ของchar (Complete!)

ตัวอย่างที่ 1337

  • เริ่มที่ตัวระบุ แต่ตอนนี้เราไปได้แล้ว!

    const char* const* (*foo[8])()
                            ^^^

    foo คืออาร์เรย์ของ 8 ...

  • กดวงเล็บเพื่อให้ไปทางขวาไม่ได้แล้วไปทางซ้าย

    const char* const* (*foo[8])()
                        ^

    foo คืออาร์เรย์ของตัวชี้ 8 ตัวเพื่อ ...

  • ในวงเล็บเสร็จแล้วก็ไปได้เลย

    const char* const* (*foo[8])()
                                ^^

    foo คืออาร์เรย์ของตัวชี้ 8 ตัวเพื่อทำหน้าที่ส่งกลับ ...

  • ไปทางขวาไม่มีอะไรมากไปทางซ้าย

    const char* const* (*foo[8])()
                     ^

    foo คืออาร์เรย์ของตัวชี้ 8 ตัวเพื่อทำหน้าที่ส่งกลับตัวชี้ไปที่ ...

  • ไปทางซ้าย

    const char* const* (*foo[8])()
                ^^^^^

    foo เป็นอาร์เรย์ของตัวชี้ 8 ตัวสำหรับฟังก์ชันที่ส่งกลับตัวชี้เป็นค่าคงที่ ...

  • ไปทางซ้าย

    const char* const* (*foo[8])()
              ^

    foo คืออาร์เรย์ของตัวชี้ 8 ตัวสำหรับฟังก์ชันที่ส่งกลับตัวชี้ไปยังตัวชี้ค่าคงที่ไปยัง ...

  • ไปทางซ้าย

    const char* const* (*foo[8])()
          ^^^^

    foo คืออาร์เรย์ของตัวชี้ 8 ตัวสำหรับฟังก์ชันที่ส่งกลับตัวชี้ไปยังตัวชี้ค่าคงที่ไปยังถ่าน ...

  • ไปทางซ้าย

    const char* const* (*foo[8])()
    ^^^^^

    foo คืออาร์เรย์ของตัวชี้ 8 ตัวไปยังฟังก์ชันที่ส่งกลับตัวชี้ไปยังตัวชี้ค่าคงที่ไปยังค่าคงที่ของถ่าน(Complete!)

คำอธิบายเพิ่มเติม: http://www.unixwiz.net/techtips/reading-cdecl.html


CMIIW, const char * const foo ควรเทียบเท่ากับ char const * const foo?
luochenhuan

@luochenhuan ใช่จริงๆพวกเขา
Garrett

12

หลายคนแนะนำให้อ่านตัวระบุประเภทจากขวาไปซ้าย

const char * // Pointer to a `char` that is constant, it can't be changed.
const char * const // A const pointer to const data.

ในทั้งสองรูปแบบตัวชี้จะชี้ไปที่ข้อมูลคงที่หรืออ่านอย่างเดียว

ในรูปแบบที่สองไม่สามารถเปลี่ยนตัวชี้ได้ ตัวชี้จะชี้ไปที่เดิมเสมอ


3

ความแตกต่างคือหากไม่มีสิ่งพิเศษconstโปรแกรมเมอร์สามารถเปลี่ยนแปลงได้ภายในวิธีการที่ตัวชี้ชี้ไปที่; ตัวอย่างเช่น:

 void print_string(const char * the_string)
 {
    cout << the_string << endl;
    //....
    the_string = another_string();
    //....

 }

นั่นจะเป็นการผิดกฎหมายแทนหากมีลายเซ็น void print_string(const char * const the_string)

โปรแกรมเมอร์หลายคนรู้สึกว่าคำสำคัญเกินจริง (ในสถานการณ์ส่วนใหญ่) constและละเว้นคำหลักนั้นแม้ว่ามันจะถูกต้องตามความหมายก็ตาม


2

ในภายหลังคุณรับประกันว่าจะไม่แก้ไขทั้งตัวชี้และตัวอักษรในตอนแรกคุณรับประกันได้ว่าเนื้อหาจะไม่เปลี่ยนแปลง แต่คุณสามารถย้ายตัวชี้ไปรอบ ๆ


อ่าถ้าไม่มี const สุดท้ายฉันจะตั้งค่าตัวชี้ให้ชี้ไปที่สตริงที่ต่างกันโดยสิ้นเชิงได้หรือไม่?
PICT

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

2

ไม่มีเหตุผลใดที่ทั้งสองจะไม่ทำงาน ทั้งหมดprint_string()คือพิมพ์ค่า มันไม่พยายามแก้ไข

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

สำหรับไวยากรณ์ที่แน่นอนคุณต้องการระบุว่าอาร์กิวเมนต์ประเภทใด "ปลอดภัย" ที่จะส่งผ่านไปยังฟังก์ชัน


2

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


2

const char *หมายความว่าคุณไม่สามารถใช้ตัวชี้เพื่อเปลี่ยนสิ่งที่ชี้ไปได้ คุณสามารถเปลี่ยนตัวชี้ให้ชี้ไปที่อย่างอื่นได้

พิจารณา:

const char * promptTextWithDefault(const char * text)
{
    if ((text == NULL) || (*text == '\0'))
        text = "C>";
    return text;
}

พารามิเตอร์เป็นตัวชี้ที่ไม่ใช่ const ไปยัง const char ดังนั้นจึงสามารถเปลี่ยนเป็นconst char *ค่าอื่นได้(เช่นสตริงคงที่) อย่างไรก็ตามหากเราเขียนผิด*text = '\0'เราก็จะได้รับข้อผิดพลาดในการคอมไพล์

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

BTW: มันเป็นวิธีที่ดีที่จะหลีกเลี่ยงchar const *เพราะมันมักอ่านผิด - มันหมายความว่าเช่นเดียวแต่หลายคนก็อ่านมันเป็นความหมายconst char *char * const


ว้าว! ฉันพยายามค้นหาความแตกต่างระหว่างconst char *ลายเซ็นของฉันกับลายเซ็นchar const *- วิธีที่คุณพูด BTW ของคุณช่วยได้มาก!
ปราชญ์

1

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

void foo(const int x);

ประกาศฟังก์ชันเดียวกับ

void foo(int x);

เฉพาะในคำจำกัดความของฟังก์ชันเท่านั้นที่constมีความหมายพิเศษ:

void foo(const int x) {
    // do something with x here, but you cannot change it
}

คำจำกัดความนี้เข้ากันได้กับการประกาศข้างต้น ผู้โทรไม่สนใจนั่นxคือconstรายละเอียดการใช้งานที่ไม่เกี่ยวข้องในไซต์การโทร

หากคุณมีconstตัวชี้ไปที่constข้อมูลจะใช้กฎเดียวกัน:

// these declarations are equivalent
void print_string(const char * const the_string);
void print_string(const char * the_string);

// In this definition, you cannot change the value of the pointer within the
// body of the function.  It's essentially a const local variable.
void print_string(const char * const the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // COMPILER ERROR HERE
}

// In this definition, you can change the value of the pointer (but you 
// still can't change the data it's pointed to).  And even if you change
// the_string, that has no effect outside this function.
void print_string(const char * the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // OK, but not observable outside this func
}

โปรแกรมเมอร์ C ++ เพียงไม่กี่คนที่ต้องการสร้างพารามิเตอร์constแม้ว่าจะเป็นไปได้ก็ตามไม่ว่าพารามิเตอร์เหล่านั้นจะเป็นตัวชี้หรือไม่ก็ตาม


"คอมไพลเลอร์จะเพิกเฉยเป็นหลัก" ไม่เป็นความจริงเสมอไป Visual C ++ 2015 จะสร้างคำเตือนหากคุณเพิ่มส่วนเกินconstให้กับพารามิเตอร์ฟังก์ชันของคุณในคำจำกัดความ แต่ไม่ประกาศ
raymai97

@ raymai97: ฉันคิดว่านั่นเป็นข้อผิดพลาดในคอมไพเลอร์ปี 2015 แต่ฉันไม่มีปี 2015 ที่สะดวกในการทดสอบ ฉันทำงานตามที่อธิบายไว้ในปี 2560 ซึ่งตามการสนทนาของฉันกับผู้เชี่ยวชาญด้านมาตรฐานบางคนเป็นพฤติกรรมที่คาดหวัง
Adrian McCarthy

-1

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


ฉันไม่ได้ถามความแตกต่างระหว่าง char * และ const char * ฉันกำลังถามระหว่าง const char * และ const char * const
pict

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