ทำไมฉันถึงได้รับข้อผิดพลาดในการแบ่งส่วนเมื่อเขียนถึง“ char * s” ที่เริ่มต้นด้วยตัวอักษรสตริง แต่ไม่ใช่“ char s []”


288

รหัสต่อไปนี้ได้รับ seg ข้อผิดพลาดในบรรทัด 2:

char *str = "string";
str[0] = 'z';  // could be also written as *str = 'z'
printf("%s\n", str);

ขณะนี้ใช้งานได้ดีอย่างสมบูรณ์:

char str[] = "string";
str[0] = 'z';
printf("%s\n", str);

ทดสอบกับ MSVC และ GCC


1
มันตลก - แต่นี่เป็นการรวบรวมและทำงานอย่างสมบูรณ์แบบเมื่อใช้ windows compiler (cl) บนพรอมต์คำสั่งสำหรับนักพัฒนา visual Studio ทำให้ฉันสับสนสักครู่ ...
David Refaeli

คำตอบ:


242

ดูคำถามที่พบบ่อย C คำถาม 1.32

ถาม : ความแตกต่างระหว่างการเริ่มต้นเหล่านี้คืออะไร โปรแกรมของฉันเกิดปัญหาถ้าผมพยายามที่จะกำหนดค่าใหม่เพื่อ
char a[] = "string literal";
char *p = "string literal";
p[i]

ตอบ : สตริงตัวอักษร (คำที่เป็นทางการสำหรับสตริงที่มีเครื่องหมายคำพูดคู่ในแหล่งข้อมูล C) สามารถใช้ได้สองวิธีแตกต่างกันเล็กน้อย:

  1. ในฐานะที่เป็น initializer สำหรับอาร์เรย์ของถ่านเช่นเดียวกับในการประกาศchar a[]มันระบุค่าเริ่มต้นของตัวละครในอาร์เรย์นั้น (และถ้าจำเป็นขนาดของมัน)
  2. ที่อื่นมันกลายเป็นอาเรย์ที่ไม่มีชื่อของชื่อสแตติกและอาเรย์ที่ไม่ได้ตั้งชื่อนี้อาจถูกเก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียวและทำให้ไม่สามารถแก้ไขได้ ในบริบทการแสดงออกอาร์เรย์จะถูกแปลงทันทีเป็นตัวชี้ตามปกติ (ดูหัวข้อ 6) ดังนั้นการประกาศครั้งที่สองจะเริ่มต้น p เพื่อชี้ไปที่องค์ประกอบแรกของอาร์เรย์ที่ไม่มีชื่อ

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


7
สองประเด็นอื่น ๆ : (1) segfault เกิดขึ้นตามที่อธิบายไว้ แต่เหตุการณ์ที่เกิดขึ้นเป็นหน้าที่ของสภาพแวดล้อมการทำงาน หากรหัสเดียวกันอยู่ในระบบฝังตัวการเขียนอาจไม่มีผลหรือจริง ๆ แล้วอาจเปลี่ยน s เป็น z (2) เนื่องจากตัวอักษรสตริงไม่สามารถเขียนได้คอมไพเลอร์สามารถประหยัดพื้นที่ได้โดยใส่ "string" สองตัวไว้ในที่เดียวกัน หรือถ้ามีที่อื่นในรหัสที่คุณมี "สายอื่น" หน่วยความจำอันหนึ่งอันสามารถรองรับตัวอักษรทั้งสองได้ เห็นได้ชัดว่าหากรหัสได้รับอนุญาตให้เปลี่ยนไบต์เหล่านั้นข้อผิดพลาดที่แปลกและยากอาจเกิดขึ้นได้
greggo

1
@greggo: จุดดี นอกจากนี้ยังมีวิธีการทำเช่นนี้ในระบบที่มี MMU โดยใช้mprotectการป้องกันการอ่านอย่างเดียว wave (ดูที่นี่ )

ดังนั้นถ่าน * p = "blah" จึงสร้างอาร์เรย์ชั่วคราวจริง ๆ
rahul tyagi

1
และหลังจากเขียน 2 ปีใน C ++ ... TIL
zeboidlund

@rahultyagi คุณหมายความว่าอย่างไร
Suraj Jain

105

โดยปกติตัวอักษรสตริงจะถูกเก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียวเมื่อเรียกใช้โปรแกรม นี่คือการป้องกันไม่ให้คุณเปลี่ยนค่าคงที่สตริงโดยไม่ตั้งใจ ในตัวอย่างแรกของคุณ"string"จะถูกเก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียวและ*strชี้ไปที่อักขระแรก segfault เกิดขึ้นเมื่อคุณพยายามเปลี่ยนอักขระตัวแรกเป็น'z'เกิดขึ้นเมื่อคุณพยายามที่จะเปลี่ยนแปลงตัวอักษรตัวแรกไป

ในตัวอย่างที่สองสตริง"string"จะถูกคัดลอกโดยคอมไพเลอร์จากบ้านอ่านอย่างเดียวไปยังstr[]อาร์เรย์ จากนั้นจึงอนุญาตให้เปลี่ยนตัวอักษรตัวแรก คุณสามารถตรวจสอบสิ่งนี้ได้โดยพิมพ์ที่อยู่ของแต่ละคน:

printf("%p", str);

นอกจากนี้การพิมพ์ขนาดstrในตัวอย่างที่สองจะแสดงให้คุณเห็นว่าคอมไพเลอร์ได้จัดสรร 7 ไบต์สำหรับมัน:

printf("%d", sizeof(str));

13
เมื่อใดก็ตามที่ใช้ "% p" ใน printf คุณควรใช้ตัวชี้เพื่อโมฆะ * เช่นเดียวกับ printf ("% p", (void *) str); เมื่อพิมพ์ size_t ด้วย printf คุณควรใช้ "% zu" หากใช้มาตรฐาน C ล่าสุด (C99)
Chris Young

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


34

คำตอบเหล่านี้ส่วนใหญ่ถูกต้อง แต่เพียงเพิ่มความชัดเจนมากขึ้น ...

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

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


แต่จริงหรือไม่ที่อาจมีการใช้งานที่อนุญาตให้แก้ไข "หน่วยความจำแบบอ่านอย่างเดียว" ได้หรือไม่
Pacerier

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

27

ทำไมฉันถึงได้รับข้อผิดพลาดการแบ่งส่วนเมื่อเขียนไปยังสตริง?

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

    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

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

 char s[] = "abc";

เราได้รับ:

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

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

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

readelf -l a.out

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

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

17

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

ในรหัสที่สอง "string" เป็นตัวกำหนดค่าเริ่มต้นของอาเรย์ซึ่งเป็นชื่อย่อของ

char str[7] =  { 's', 't', 'r', 'i', 'n', 'g', '\0' };

"str" ​​คืออาร์เรย์ที่จัดสรรในสแต็กและสามารถแก้ไขได้อย่างอิสระ


1
ในกองหรือส่วนข้อมูลถ้าstrเป็น บริษัท staticระดับโลกหรือ
Gauthier

12

เพราะประเภท"whatever"ในบริบทของตัวอย่างที่ 1 คือconst char * (แม้ว่าคุณจะกำหนดให้กับผู้ที่ไม่ใช่ const *) ซึ่งหมายความว่าคุณไม่ควรลองและเขียนมัน

คอมไพเลอร์ได้บังคับใช้สิ่งนี้โดยการใส่สตริงในส่วนของหน่วยความจำแบบอ่านอย่างเดียวดังนั้นการเขียนลงในมันจะสร้าง segfault


8

เพื่อทำความเข้าใจข้อผิดพลาดหรือปัญหานี้คุณควรทราบความแตกต่าง b / w ตัวชี้และอาร์เรย์ดังนั้นที่นี่ก่อนอื่นฉันได้อธิบายคุณแตกต่าง b / w พวกเขา

อาร์เรย์สตริง

 char strarray[] = "hello";

ในหน่วยความจำอาร์เรย์จะถูกเก็บไว้ในเซลล์หน่วยความจำอย่างต่อเนื่องซึ่งจัดเก็บเป็น[h][e][l][l][o][\0] =>[]เซลล์หน่วยความจำขนาดถ่านขนาด 1 ไบต์และเซลล์หน่วยความจำต่อเนื่องนี้สามารถเข้าถึงได้โดยใช้ชื่อ strarray ที่นี่ดังนั้นนี่คืออาร์เรย์สตริงstrarrayที่ประกอบด้วยอักขระทั้งหมด กรณีที่นี่"hello" เพื่อให้เราสามารถเปลี่ยนเนื้อหาหน่วยความจำได้อย่างง่ายดายโดยการเข้าถึงตัวละครแต่ละตัวด้วยค่าดัชนี

`strarray[0]='m'` it access character at index 0 which is 'h'in strarray

และค่าของมันเปลี่ยนเป็น'm'ค่า strarray จึงเปลี่ยนเป็น"mello" ;

จุดหนึ่งที่จะต้องทราบที่นี่ว่าเราสามารถเปลี่ยนเนื้อหาของสตริงอาเรย์โดยการเปลี่ยนตัวอักษรโดยตัวละคร แต่ไม่สามารถเริ่มต้นสตริงอื่น ๆ โดยตรงเช่น strarray="new string"ไม่ถูกต้อง

ชี้

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

char *ptr = "hello";

ptr ของตัวชี้ที่นี่ถูกเตรียมใช้งานเป็นสตริง"hello"ซึ่งเป็นสตริงคงที่ที่เก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียว (ROM) ดังนั้นจึง"hello"ไม่สามารถเปลี่ยนแปลงได้เนื่องจากมันถูกเก็บไว้ใน ROM

และ PTR จะถูกเก็บไว้ในส่วนสแต็คและชี้ไปที่สตริงคงที่ "hello"

ดังนั้น ptr [0] = 'm' ไม่ถูกต้องเนื่องจากคุณไม่สามารถเข้าถึงหน่วยความจำแบบอ่านอย่างเดียว

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

ptr="new string"; is valid

7
char *str = "string";  

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

ดังนั้นstr[0]=พยายามที่จะเขียนไปยังรหัสอ่านอย่างเดียวของแอปพลิเคชัน ฉันเดาว่านี่น่าจะเป็นคอมไพเลอร์แล้วแต่


6
char *str = "string";

จัดสรรตัวชี้ไปที่ตัวอักษรสตริงซึ่งคอมไพเลอร์จะใส่ในส่วนที่ไม่สามารถแก้ไขได้ของปฏิบัติการของคุณ;

char str[] = "string";

จัดสรรและเริ่มต้นอาร์เรย์ท้องถิ่นที่สามารถแก้ไขได้


เราเขียนได้ไหม int *b = {1,2,3) เหมือนที่เราเขียนได้char *s = "HelloWorld"ไหม
Suraj Jain

6

คำถามที่พบบ่อย C @ @lili เชื่อมโยงกับกล่าวถึง แต่ยังไม่มีใครที่นี่ดังนั้นสำหรับการชี้แจง: ถ้าสตริงตัวอักษร (สตริงสองครั้งที่ยกมาในแหล่งที่มาของคุณ) จะใช้ที่อื่นนอกเหนือจากการเริ่มต้นอาร์เรย์อักขระ (เช่น: @ ตัวอย่างที่สองของ Mark ซึ่งทำงานอย่างถูกต้อง) สตริงนั้นถูกจัดเก็บโดยคอมไพเลอร์ในตารางสตริงแบบคงที่พิเศษซึ่งคล้ายกับการสร้างตัวแปรสแตติกแบบคงที่ทั่วโลก (แน่นอนว่าอ่านอย่างเดียว) ที่ไม่ระบุชื่อเป็นหลัก ") ส่วนแบบอ่านอย่างเดียวเป็นส่วนที่สำคัญและเป็นสาเหตุที่ segfaults โค้ดแรกของ @ Mark


เราเขียนint *b = {1,2,3) เหมือนที่เราเขียนได้char *s = "HelloWorld"ไหม
Suraj Jain

4

 char *str = "string";

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

  str[0] = 'z';

คุณได้รับความผิดพลาด ในบางแพลตฟอร์มตัวอักษรอาจอยู่ในหน่วยความจำที่เขียนได้ดังนั้นคุณจะไม่เห็น segfault แต่เป็นรหัสที่ไม่ถูกต้อง

เส้น:

char str[] = "string";

จัดสรรอาร์เรย์ของอักขระและคัดลอกสตริงตัวอักษรลงในอาร์เรย์นั้นซึ่งสามารถเขียนได้อย่างสมบูรณ์ดังนั้นการปรับปรุงที่ตามมาจึงไม่มีปัญหา


เราเขียนint *b = {1,2,3) เหมือนที่เราเขียนได้char *s = "HelloWorld"ไหม
Suraj Jain

3

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

ในตัวอย่างแรกของคุณคุณจะได้รับตัวชี้ไปยังข้อมูล const นั้น ในตัวอย่างที่สองของคุณคุณกำลังเริ่มต้นอาร์เรย์จำนวน 7 อักขระด้วยสำเนาข้อมูล const


2
// create a string constant like this - will be read only
char *str_p;
str_p = "String constant";

// create an array of characters like this 
char *arr_p;
char arr[] = "String in an array";
arr_p = &arr[0];

// now we try to change a character in the array first, this will work
*arr_p = 'E';

// lets try to change the first character of the string contant
*str_p = 'G'; // this will result in a segmentation fault. Comment it out to work.


/*-----------------------------------------------------------------------------
 *  String constants can't be modified. A segmentation fault is the result,
 *  because most operating systems will not allow a write
 *  operation on read only memory.
 *-----------------------------------------------------------------------------*/

//print both strings to see if they have changed
printf("%s\n", str_p); //print the string without a variable
printf("%s\n", arr_p); //print the string, which is in an array. 

1

ในสถานที่แรกที่เป็นตัวชี้ว่าจุดstr "string"คอมไพเลอร์ได้รับอนุญาตให้ใส่ตัวอักษรสตริงในสถานที่ในหน่วยความจำที่คุณไม่สามารถเขียน แต่สามารถอ่านได้เท่านั้น (สิ่งนี้น่าจะเรียกคำเตือนจริง ๆ เนื่องจากคุณได้มอบหมายconst char *ให้ a char *. คุณได้ปิดการเตือนหรือคุณเพิกเฉยมัน)

"string"ในสถานที่ที่สองคุณกำลังสร้างอาร์เรย์ซึ่งเป็นหน่วยความจำที่คุณได้มีการเข้าถึงแบบเต็มไปและเริ่มต้นด้วย คุณกำลังสร้างchar[7](หกตัวอักษรหนึ่งตัวสำหรับการยกเลิก '\ 0') และคุณทำสิ่งที่คุณต้องการ


@Ferruccio ใช่constคำนำหน้าทำให้ตัวแปรอ่านอย่างเดียว
EsmaeelE

ใน C สตริงตัวอักษรมีประเภทchar [N]ไม่const char [N]ดังนั้นจึงไม่มีการเตือน (คุณสามารถเปลี่ยนสิ่งนั้นในหน่วย gcc อย่างน้อยก็ให้ผ่าน-Wwrite-strings)
melpomene

0

สมมติว่าเป็นสตริง

char a[] = "string literal copied to stack";
char *p  = "string literal referenced by p";

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

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


-1

แรกคือหนึ่งสตริงคงที่ซึ่งไม่สามารถแก้ไขได้ ประการที่สองคืออาร์เรย์ที่มีค่าเริ่มต้นดังนั้นจึงสามารถแก้ไขได้


-2

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

char *str เป็นตัวชี้ไปยังสตริงที่ไม่สามารถแก้ไขได้ (เหตุผลในการรับ segfault)

ในขณะchar str[]ที่อาร์เรย์และสามารถแก้ไขได้ ..

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