char s [] และ char * s แตกต่างกันอย่างไร?


506

ใน C หนึ่งสามารถใช้ตัวอักษรสตริงในการประกาศเช่นนี้:

char s[] = "hello";

หรือเช่นนี้

char *s = "hello";

ดังนั้นความแตกต่างคืออะไร? ฉันต้องการที่จะรู้ว่าสิ่งที่เกิดขึ้นจริงในแง่ของระยะเวลาการจัดเก็บทั้งในเวลารวบรวมและเวลาทำงาน



8
ถ่าน * s = "hello" ที่นี่ s สามารถชี้สตริงอื่น ณ รันไทม์ฉันหมายความว่ามันไม่ใช่ตัวชี้คงที่คุณสามารถกำหนดค่าอื่น ณ รันไทม์ p = "Nishant" ในขณะที่ s [] นี่คือตัวชี้คงที่ .. .. มันไม่สามารถกำหนดสตริงใหม่อีกครั้งได้ แต่เราสามารถกำหนดค่าอักขระอื่นได้ที่ s [ดัชนี]
Nishant Kumar

คำตอบ:


541

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

char *s = "Hello world";

จะวาง"Hello world"ในส่วนอ่านอย่างเดียวของหน่วยความจำและทำให้sตัวชี้ไปยังที่ทำให้การเขียนใด ๆ ในหน่วยความจำนี้ผิดกฎหมาย

ในขณะที่ทำ:

char s[] = "Hello world";

วางสตริงตัวอักษรในหน่วยความจำแบบอ่านอย่างเดียวและคัดลอกสตริงไปยังหน่วยความจำที่จัดสรรใหม่บนสแต็ก จึงทำให้

s[0] = 'J';

ถูกกฎหมาย


22
สตริงตัวอักษร"Hello world"อยู่ใน "ส่วนอ่านอย่างเดียวของหน่วยความจำ" ในทั้งสองตัวอย่าง ตัวอย่างที่มีอาเรย์ชี้ไปที่นั่นตัวอย่างของอาเรย์จะคัดลอกอักขระไปยังองค์ประกอบอาเรย์
pmg

28
pmg: ในกรณีที่สองสตริงตัวอักษรไม่จำเป็นต้องอยู่ในหน่วยความจำในฐานะที่เป็นวัตถุที่ต่อเนื่องกันเพียงอย่างเดียว - เป็นเพียงตัวเริ่มต้นคอมไพเลอร์อาจปล่อยชุดคำสั่ง "โหลดทันทีไบต์" เป็นชุดที่มีค่าอักขระฝังอยู่ภายใน พวกเขา
caf

10
ตัวอย่างอาร์เรย์ถ่านไม่จำเป็นต้องวางสตริงบนสแต็ก - หากปรากฏในระดับไฟล์มันอาจจะอยู่ในส่วนของข้อมูลเริ่มต้นบางส่วนแทน
caf

9
ฉันต้องการชี้ให้เห็นว่า char s = "xx" ไม่จำเป็นต้องอยู่ในหน่วยความจำแบบอ่านอย่างเดียว (การใช้งานบางอย่างไม่มี MMU เช่น) ร่างของ n1362 c1x เพียงระบุว่าการแก้ไขอาเรย์เป็นสาเหตุของพฤติกรรมที่ไม่ได้กำหนด แต่ +1 อย่างไรก็ตามเนื่องจากการพึ่งพาพฤติกรรมนั้นเป็นสิ่งที่โง่ที่จะทำ
paxdiablo

3
ฉันได้รับการคอมไพล์สะอาดในไฟล์ที่มีเพียงchar msg[] = "hello, world!"; สตริงสิ้นสุดในส่วนข้อมูลเริ่มต้น เมื่อประกาศว่าchar * constจะลงเอยในส่วนข้อมูลแบบอ่านอย่างเดียว gcc-4.5.3
gcbenison

152

ก่อนอื่นในฟังก์ชันอาร์กิวเมนต์พวกเขาจะเทียบเท่า:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

ในบริบทอื่น ๆchar *จัดสรรตัวชี้ในขณะที่char []จัดสรรอาร์เรย์ สตริงจะไปไหนในกรณีก่อนหน้าคุณถาม? คอมไพเลอร์จัดสรรอาร์เรย์แบบไม่ระบุชื่อแบบสแตติกเพื่อเก็บสตริงตามตัวอักษรอย่างลับ ๆ ดังนั้น:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

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

x[1] = 'O'; // BAD. DON'T DO THIS.

การใช้ไวยากรณ์อาร์เรย์จะจัดสรรให้โดยตรงในหน่วยความจำใหม่ ดังนั้นการปรับเปลี่ยนจึงปลอดภัย:

char x[] = "Foo";
x[1] = 'O'; // No problem.

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


72

คำประกาศนี้:

char s[] = "hello";

สร้างหนึ่งในวัตถุ - เป็นcharอาร์เรย์ของขนาด 6 เรียกว่าs, initialised 'h', 'e', 'l', 'l', 'o', '\0'ด้วยค่า ที่จัดสรรอาร์เรย์นี้ในหน่วยความจำและนานเท่าไรขึ้นอยู่กับที่ประกาศจะปรากฏขึ้น หากการประกาศอยู่ภายในฟังก์ชั่นมันจะยังคงอยู่จนกว่าจะสิ้นสุดของบล็อกที่มีการประกาศในและเกือบจะแน่นอนได้รับการจัดสรรในกอง; ถ้ามันอยู่นอกฟังก์ชั่นมันอาจจะถูกเก็บไว้ใน "ส่วนเริ่มต้นข้อมูล" ที่โหลดจากไฟล์ปฏิบัติการลงในหน่วยความจำที่เขียนได้เมื่อเรียกใช้โปรแกรม

ในทางตรงกันข้ามการประกาศนี้:

char *s ="hello";

สร้างวัตถุสองรายการ :

  • อ่านอย่างเดียวอาร์เรย์ของ 6 chars ที่มีค่า'h', 'e', 'l', 'l', 'o', '\0'ซึ่งไม่มีชื่อและมีระยะเวลาในการจัดเก็บข้อมูลแบบคงที่ (หมายถึงว่ามันอาศัยสำหรับชีวิตทั้งหมดของโปรแกรม); และ
  • ตัวแปรของชนิดพอยน์ - ทู - ชาsร์ซึ่งถูกกำหนดค่าเริ่มต้นด้วยตำแหน่งของอักขระตัวแรกในอาเรย์แบบอ่านอย่างเดียวที่ไม่มีชื่อ

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


1
ในการประกาศทั้งสองสำหรับหน่วยความจำ "hello" ถูกจัดสรรในเวลา comiple? และสิ่งอื่นถ่าน * p = "hello" ที่นี่ "hello" ถูกเก็บไว้ในส่วนข้อความตามที่คุณระบุไว้ในคำตอบของคุณ ... และสิ่งที่เกี่ยวกับถ่าน s [] = "hello" จะเก็บไว้เป็นอันดับแรกในส่วนของข้อความและในระหว่างรันไทม์มันจะคัดลอกเป็นกองตามที่ Rickard ระบุไว้ในคำตอบที่นั่น กรุณาชี้แจงจุดนี้
Nishant Kumar

2
@Nishant: ในchar s[] = "hello"กรณีที่"hello"เป็นเพียงผู้เริ่มต้นบอกคอมไพเลอร์ว่าควรจะเริ่มต้นอาร์เรย์ มันอาจหรือไม่อาจส่งผลให้สตริงที่สอดคล้องกันในส่วนของข้อความ - ตัวอย่างเช่นถ้าsมีระยะเวลาการจัดเก็บแบบคงที่ก็มีโอกาสที่อินสแตนซ์เดียวของ"hello"จะอยู่ในส่วนข้อมูลเริ่มต้น - วัตถุsเอง แม้ว่าจะsมีระยะเวลาการเก็บข้อมูลอัตโนมัติ แต่ก็สามารถเริ่มต้นได้ด้วยลำดับของที่เก็บที่แท้จริงแทนที่จะเป็นสำเนา (เช่นmovl $1819043176, -6(%ebp); movw $111, -2(%ebp))
คาเฟ่

อีกอย่างแม่นยำ GCC 4.8 ทำให้มันเป็นที่สคริปต์ลิงเกอร์แล้วทิ้งลงไปในกลุ่มเดียวกันเป็น.rodata .textดูคำตอบของฉัน
Ciro Santilli 法轮功冠状病六四事件法轮功

@caf ในคำตอบแรกโดย Rickard เป็นลายลักษณ์อักษรที่char s[] = "Hello world";ใส่สตริงตัวอักษรในหน่วยความจำแบบอ่านอย่างเดียวและคัดลอกสตริงไปยังหน่วยความจำที่จัดสรรใหม่บนสแต็ก แต่คำตอบของคุณเพียง copies the string to newly allocated memory on the stackแต่พูดเกี่ยวกับการใส่สตริงตัวอักษรในหน่วยความจำอ่านอย่างเดียวและข้ามส่วนที่สองของประโยคที่กล่าวว่า: ดังนั้นคำตอบของคุณไม่สมบูรณ์สำหรับการไม่ระบุส่วนที่สอง?
KPMG

1
@AjaySinghNegi: ตามที่ฉันได้ระบุไว้ในความคิดเห็นอื่น (สำหรับคำตอบนี้และคำตอบของ Rickard) สตริงในchar s[] = "Hellow world";นั้นเป็นเพียงตัวเริ่มต้นและไม่จำเป็นต้องเก็บไว้เป็นสำเนาแบบอ่านอย่างเดียวเลย หากsมีระยะเวลาการจัดเก็บแบบคงที่สำเนาเดียวของสตริงน่าจะอยู่ในส่วนอ่าน - เขียนที่ตำแหน่งของsและแม้ว่าจะไม่ใช่จากนั้นคอมไพเลอร์อาจเลือกที่จะเริ่มต้นอาร์เรย์ด้วยคำแนะนำโหลดทันทีหรือคล้ายกันมากกว่าการคัดลอก จากสตริงอ่านอย่างเดียว ประเด็นก็คือในกรณีนี้สตริงตัวเริ่มต้นเองไม่มีสถานะรันไทม์
caf

60

รับการประกาศ

char *s0 = "hello world";
char s1[] = "hello world";

สมมติแผนที่หน่วยความจำสมมุติต่อไปนี้:

                    0x01 0x02 0x03 0x04
        0x00008000: 'h' 'e' 'l' 'l'
        0x00008004: 'o' '' 'w' 'o'
        0x00008008: 'r' 'l' 'd' 0x00
        ...
s0: 0x00010000: 0x00 0x00 0x80 0x00
s1: 0x00010004: 'h' 'e' 'l' 'l'
        0x00010008: 'o' '' 'w' 'o'
        0x0001000C: 'r' 'l' 'd' 0x00

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

เส้น

char *s0 = "hello world";

กำหนดs0เป็นตัวชี้ไปcharด้วยระยะเวลาการจัดเก็บอัตโนมัติ (หมายถึงตัวแปรที่s0มีอยู่เฉพาะสำหรับขอบเขตที่มีการประกาศ) และคัดลอกที่อยู่ของสตริงตัวอักษร ( 0x00008000ในตัวอย่างนี้) ไปที่มัน หมายเหตุว่าตั้งแต่s0ชี้ไปยังตัวอักษรสตริงมันไม่ควรถูกนำมาใช้เป็นอาร์กิวเมนต์ฟังก์ชั่นใด ๆ ที่จะพยายามที่จะปรับเปลี่ยนได้ (เช่น, strtok(), strcat(), strcpy()ฯลฯ )

เส้น

char s1[] = "hello world";

กำหนดs1เป็นอาร์เรย์ 12 องค์ประกอบของchar(ความยาวถูกนำมาจากสตริงตัวอักษร) ด้วยระยะเวลาการจัดเก็บอัตโนมัติและคัดลอกเนื้อหาของตัวอักษรไปยังอาร์เรย์ ที่คุณสามารถดูจากแผนที่หน่วยความจำที่เรามีสองสำเนาของสตริง"hello world"; s1ความแตกต่างคือการที่คุณสามารถปรับเปลี่ยนสตริงที่มีอยู่ใน

s0และs1สามารถใช้แทนกันได้ในบริบทส่วนใหญ่ นี่คือข้อยกเว้น:

sizeof s0 == sizeof (char*)
sizeof s1 == 12

type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char

คุณสามารถกำหนดตัวแปรs0ให้ชี้ไปที่สตริงตัวอักษรอื่นหรือตัวแปรอื่น คุณไม่สามารถกำหนดตัวแปรs1ให้ชี้ไปยังอาร์เรย์อื่นได้


2
ฉันคิดว่าแผนที่หน่วยความจำสมมุติทำให้เข้าใจง่าย!
เที่ยงคืนบลู

32

C99 N1256 ฉบับร่าง

มีการใช้ตัวอักษรสตริงของอักขระสองแบบ:

  1. เริ่มต้นchar[]:

    char c[] = "abc";      

    นี่คือ "มายากลเพิ่มเติม" และอธิบายไว้ที่ 6.7.8 / 14 "การเริ่มต้น":

    อาเรย์ของประเภทตัวละครอาจเริ่มต้นได้โดยสตริงตัวอักษรตัวเลือกล้อมรอบด้วยวงเล็บปีกกา อักขระต่อเนื่องของสตริงอักขระตามตัวอักษร (รวมถึงอักขระ null สิ้นสุดหากมีที่ว่างหรือถ้าอาร์เรย์มีขนาดไม่ทราบค่า) เริ่มต้นองค์ประกอบของอาร์เรย์

    ดังนั้นนี่เป็นเพียงทางลัดสำหรับ:

    char c[] = {'a', 'b', 'c', '\0'};

    เช่นเดียวกับอาเรย์ทั่วไปอื่น ๆ ที่cสามารถแก้ไขได้

  2. ทุกที่อื่น: มันสร้าง:

    ดังนั้นเมื่อคุณเขียน:

    char *c = "abc";

    สิ่งนี้คล้ายกับ:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;

    บันทึกการส่งข้อมูลโดยนัยจากchar[]ไปถึงchar *ซึ่งถูกกฎหมายเสมอ

    จากนั้นถ้าคุณแก้ไขc[0]คุณก็จะแก้ไข__unnamedซึ่งก็คือ UB

    นี่คือเอกสารที่ 6.4.5 "ตัวอักษรสตริง":

    5 ในการแปลเฟส 7 ไบต์หรือรหัสของค่าศูนย์จะถูกผนวกเข้ากับลำดับอักขระหลายไบต์ที่เป็นผลมาจากสตริงตัวอักษรหรือตัวอักษร ลำดับอักขระแบบมัลติไบต์จะถูกใช้เพื่อเริ่มต้นอาร์เรย์ของระยะเวลาการจัดเก็บแบบสแตติกและความยาวเพียงพอที่จะมีลำดับ สำหรับตัวอักษรสตริงตัวอักษรองค์ประกอบอาร์เรย์มีประเภทถ่านและจะเริ่มต้นด้วยไบต์แต่ละลำดับอักขระ multibyte [... ]

    6 มันไม่ได้ระบุว่าอาร์เรย์เหล่านี้แตกต่างกันหรือไม่หากองค์ประกอบของพวกเขามีค่าที่เหมาะสม หากโปรแกรมพยายามปรับเปลี่ยนอาร์เรย์ลักษณะการทำงานจะไม่ได้กำหนด

6.7.8 / 32 "การเริ่มต้น" ให้ตัวอย่างโดยตรง:

ตัวอย่างที่ 8: การประกาศ

char s[] = "abc", t[3] = "abc";

กำหนดวัตถุอาร์เรย์ถ่าน "ธรรมดา" sและtองค์ประกอบที่มีการเริ่มต้นด้วยตัวอักษรสตริงตัวอักษร

คำประกาศนี้เหมือนกัน

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

เนื้อหาของอาร์เรย์สามารถแก้ไขได้ ในทางกลับกันการประกาศ

char *p = "abc";

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

GCC 4.8 x86-64 การใช้เอลฟ์

โปรแกรม:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

รวบรวมและถอดรหัส:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

เอาท์พุทประกอบด้วย:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

สรุป: ร้านค้า GCC char*ในส่วนไม่ได้อยู่ใน.rodata.text

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

readelf -l a.out

ซึ่งประกอบด้วย:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

ถ้าเราทำเช่นเดียวกันสำหรับchar[]:

 char s[] = "abc";

เราได้รับ:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

ดังนั้นมันจึงถูกเก็บไว้ในสแต็ก (สัมพันธ์กับ%rbp)


15
char s[] = "hello";

ประกาศว่าsเป็นอาร์เรย์charที่มีความยาวพอที่จะเก็บ initializer (5 + 1 chars) และเริ่มต้นอาร์เรย์โดยการคัดลอกสมาชิกของสตริงตัวอักษรที่กำหนดลงในอาร์เรย์

char *s = "hello";

ประกาศsที่จะเป็นตัวชี้ไปยังหนึ่งหรือมากกว่า (ในกรณีนี้มากขึ้น) charและจุดโดยตรงที่คงที่ (อ่านอย่างเดียว) "hello"สถานที่ที่มีอักษร


1
จะใช้วิธีการใดในการใช้งานฟังก์ชั่นถ้า s จะไม่เปลี่ยนแปลง f (const char s []) หรือ f (const char * s)
psihodelia

1
@psihodelia: ในการประกาศฟังก์ชั่นไม่มีความแตกต่าง ในทั้งสองกรณีเป็นตัวชี้ไปs const char
CB Bailey

4
char s[] = "Hello world";

นี่sคืออาร์เรย์ของตัวละครซึ่งสามารถเขียนทับได้หากเราต้องการ

char *s = "hello";

สตริงตัวอักษรจะใช้ในการสร้างบล็อกตัวละครเหล่านี้บางแห่งในหน่วยความจำซึ่งตัวชี้นี้sจะชี้ไปที่ เราสามารถกำหนดวัตถุที่ชี้ไปที่ใหม่ได้โดยเปลี่ยนสิ่งนั้น แต่ตราบใดที่มันชี้ไปที่สตริงตัวอักษรบล็อกของอักขระที่มันชี้ไม่สามารถเปลี่ยนแปลงได้


@bo Persson เหตุใดบล็อกของอักขระจึงไม่สามารถเปลี่ยนแปลงได้ในกรณีที่สอง
Pankaj Mahato

3

นอกจากนี้ให้พิจารณาว่าสำหรับวัตถุประสงค์แบบอ่านอย่างเดียวการใช้ทั้งสองอย่างนี้เหมือนกันคุณสามารถเข้าถึง char โดยการทำดัชนีด้วย[]หรือจัด*(<var> + <index>) รูปแบบ:

printf("%c", x[1]);     //Prints r

และ:

printf("%c", *(x + 1)); //Prints r

เห็นได้ชัดว่าถ้าคุณพยายามที่จะทำ

*(x + 1) = 'a';

คุณอาจจะได้รับการแบ่งกลุ่มผิดพลาดในขณะที่คุณพยายามเข้าถึงหน่วยความจำแบบอ่านอย่างเดียว


สิ่งนี้ไม่แตกต่างจากการใช้x[1] = 'a';segfault เช่นกัน (ขึ้นอยู่กับแพลตฟอร์มของหลักสูตร)
glglgl

3

เพียงเพิ่ม: คุณยังได้รับค่าที่แตกต่างกันสำหรับขนาดของพวกเขา

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

ดังกล่าวข้างต้นสำหรับอาร์เรย์'\0'จะถูกจัดสรรเป็นองค์ประกอบสุดท้าย


2
char *str = "Hello";

ด้านบนตั้งค่า str to ชี้ไปที่ค่าตัวอักษร "Hello" ซึ่งเป็นรหัสตายตัวในอิมเมจไบนารีของโปรแกรมซึ่งถูกตั้งค่าสถานะเป็นหน่วยความจำแบบอ่านอย่างเดียวหมายความว่าการเปลี่ยนแปลงใด ๆ ในตัวอักษรสตริงนี้ผิดกฎหมาย

char str[] = "Hello";

คัดลอกสตริงไปยังหน่วยความจำที่จัดสรรใหม่บนสแต็ก ดังนั้นการเปลี่ยนแปลงใด ๆ ในนั้นจะได้รับอนุญาตและถูกกฎหมาย

means str[0] = 'M';

จะเปลี่ยน str เป็น "Mello"

สำหรับรายละเอียดเพิ่มเติมโปรดอ่านคำถามที่คล้ายกัน:

เหตุใดฉันจึงได้รับข้อผิดพลาดการแบ่งกลุ่มเมื่อเขียนไปยังสตริงที่กำหนดค่าเริ่มต้นด้วย "char * s" แต่ไม่ใช่ "char s []"


0

ในกรณีของ:

char *x = "fred";

x คือlvalue - สามารถกำหนดให้ แต่ในกรณีของ:

char x[] = "fred";

x ไม่ใช่ lvalue มันเป็น rvalue - คุณไม่สามารถกำหนดให้มันได้


3
ในทางเทคนิคxเป็นค่า lvalue ที่ไม่สามารถแก้ไขได้ ในเกือบทุกบริบท แต่ก็จะมีการประเมินเพื่อชี้ไปยังองค์ประกอบแรกของมันและที่คุ้มค่าเป็น rvalue
caf

0
char *s1 = "Hello world"; // Points to fixed character string which is not allowed to modify
char s2[] = "Hello world"; // As good as fixed array of characters in string so allowed to modify

// s1[0] = 'J'; // Illegal
s2[0] = 'J'; // Legal

-1

ในแง่ของความคิดเห็นที่นี่มันควรจะชัดเจนว่า: char * s = "hello"; เป็นความคิดที่ไม่ดีและควรใช้ในขอบเขตที่แคบมาก

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

เรื่องประโลมโลกที่เพียงพอนี่คือสิ่งที่เราจะทำได้เมื่อ adorning pointers ด้วย "const" (หมายเหตุ: หนึ่งต้องอ่านการประกาศของตัวชี้จากขวาไปซ้าย) ต่อไปนี้เป็นวิธีที่แตกต่างกัน 3 วิธีในการป้องกันตัวเองเมื่อเล่นกับพอยน์เตอร์:

const DBJ* p means "p points to a DBJ that is const" 

- นั่นคือวัตถุ DBJ ไม่สามารถเปลี่ยนผ่าน p

DBJ* const p means "p is a const pointer to a DBJ" 

- นั่นคือคุณสามารถเปลี่ยนวัตถุ DBJ ผ่าน p แต่คุณไม่สามารถเปลี่ยนตัวชี้ p ได้

const DBJ* const p means "p is a const pointer to a const DBJ" 

- นั่นคือคุณไม่สามารถเปลี่ยนตัวชี้ p และไม่สามารถเปลี่ยนวัตถุ DBJ ผ่าน p

ข้อผิดพลาดที่เกี่ยวข้องกับการกลายพันธุ์ของ const-ant พยายามที่จะถูกรวบรวมในเวลารวบรวม ไม่มีพื้นที่ว่างรันไทม์หรือปรับความเร็วสำหรับ const

(สมมติว่าคุณใช้คอมไพเลอร์ C ++ ใช่ไหม)

--DBJ


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

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