ข้อแตกต่างระหว่าง char * และ const char *?


177

ความแตกต่างระหว่างอะไร

char* name

ซึ่งชี้ไปที่ตัวอักษรสตริงคงที่และ

const char* name

คุณหมายถึงอะไรโดย " ตัวอักษรสตริงคงที่ " ใน C (ไม่ใช่ C ++)
gbulmer

1
... ชื่อ char * สามารถทำให้ชี้ไปที่สตริงตัวอักษรคงที่
Iceman

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

แบบง่าย: ชื่อ "char * name" เป็นตัวชี้ไปยังอักขระเช่นทั้งสองสามารถเปลี่ยนแปลงได้ที่นี่ ชื่อ "const char * *" เป็นตัวชี้ไปที่ const char เช่นตัวชี้สามารถเปลี่ยนได้ แต่ไม่ใช่ตัวอักษร
akD

อ่านสิ่งเหล่านี้จากขวาไปซ้าย
Jiapeng Zhang

คำตอบ:


406

char*เป็นตัวชี้ที่เปลี่ยนแปลงไม่ได้ไปที่ไม่แน่นอนตัวอักษร / สตริง

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

char* constเป็นตัวชี้ที่ไม่เปลี่ยนรูป (ไม่สามารถชี้ไปยังตำแหน่งอื่นได้) แต่เนื้อหาของตำแหน่งที่จุดนั้นเปลี่ยนแปลงได้ได้

const char* constเป็นไม่เปลี่ยนรูปชี้ไปยังไม่เปลี่ยนรูปตัวอักษร / สตริง


4
ความสับสนสามารถลบล้างได้ด้วยการใช้ตัวแปรหลังจากข้อความดังกล่าวข้างต้นและโดยอ้างอิงถึงตัวแปรนั้น
ankit.karwasra

3
@ ankit.karwasra คุณพลาดอีกหนึ่ง:char const *
Pacerier

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

1
จะไม่char *ทำให้เกิดการแบ่งเซ็กเมนต์ผิดพลาดขณะทำงานหรือไม่
Divyanshu Maithani

1
ดังนั้นฉันจะใช้constถ้าฉันต้องการให้คอมไพเลอร์ให้ข้อผิดพลาดถ้าฉันลืมและเปลี่ยนแปลงข้อมูลโดยไม่ได้ตั้งใจใช่ไหม
นักบัญชี

43
char *name

คุณสามารถเปลี่ยนถ่านที่ nameจุดใดก็ได้และถ่านที่มันชี้ไป

const char* name

คุณสามารถเปลี่ยนถ่านให้เป็นnameจุดใด แต่คุณไม่สามารถแก้ไขถ่านที่ชี้ไปได้
การแก้ไข:คุณสามารถเปลี่ยนชี้ แต่ไม่ได้ถ่านที่nameจุดที่จะ ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspxดู "ตัวอย่าง" ) ในกรณีนี้ตัวconstระบุจะใช้กับcharไม่ใช่เครื่องหมายดอกจัน

ตามไปที่หน้า MSDN และhttp://en.cppreference.com/w/cpp/language/declarationsที่constก่อนที่จะ*เป็นส่วนหนึ่งของลำดับ decl-ระบุในขณะที่constหลังจากที่*เป็นส่วนหนึ่งของ declarator
ลำดับประกาศระบุสามารถตาม declarators หลายซึ่งเป็นเหตุผลที่const char * c1, c2ประกาศc1เป็นconst char *และเป็นc2const char

แก้ไข:

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

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

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

ตัวอย่างตัวอย่างออนไลน์:

#include <string.h>
int main()
{
    char *str1 = "string Literal";
    const char *str2 = "string Literal";
    char source[] = "Sample string";

    strcpy(str1,source);    //No warning or error, just Undefined Behavior
    strcpy(str2,source);    //Compiler issues a warning

    return 0;
}

เอาท์พุท:

cc1: คำเตือนที่ถือว่าเป็นข้อผิดพลาด
prog.c: ในฟังก์ชัน 'main':
prog.c: 9: ข้อผิดพลาด: ผ่านการโต้แย้ง 1 ของ 'strcpy' ยกเลิกตัวระบุจากประเภทเป้าหมายของตัวชี้

สังเกตว่าคอมไพเลอร์เตือนสำหรับกรณีที่สอง แต่ไม่ใช่สำหรับกรณีแรก


ขอบคุณ .. ฉันกำลังผสมกับตัวอักษรสตริงคงที่ซึ่งถูกกำหนดเป็น: char * name = "String Literal"; การเปลี่ยน "String Literal" นั้นไม่ได้กำหนด ..
Iceman

@ user1279782: เอ่อรอ! คุณกำลังพูดถึงจุดที่ชี้ไปที่ตัวอักษรสตริงที่นี่? ในกรณีดังกล่าวคุณไม่ควรแก้ไขอักขระที่เป็นnameจุดในทั้งสองกรณีมันอาจส่งผลให้เกิด UB
Alok บันทึก

ใช่นั่นคือประเด็น ดังนั้นในกรณีนั้นชื่อ char * และชื่อ char char * ทำตัวเหมือนกันใช่ไหม?
Iceman

4
คำตอบนี้ไม่ชัดเจนอย่างมากหรือผิดธรรมดา ฉันจะตีความ "คุณไม่สามารถเปลี่ยนถ่านให้เป็นชื่อของคะแนนได้ แต่คุณสามารถปรับเปลี่ยนถ่านที่ชี้ไปได้" ไม่สามารถแก้ไขตัวชี้ได้ แต่สามารถแก้ไขตำแหน่งหน่วยความจำที่ชี้ไปซึ่งไม่ถูกต้อง: ideone.com/6lUY9sหรือสำหรับบริสุทธิ์ C: ideone.com/x3PcTP
shroudednight

1
@shroudednight: คุณต้องเรียนรู้เพิ่มเติมเล็กน้อยเกี่ยวกับพฤติกรรมที่ไม่ได้กำหนดและจำเป็นต้องแยกแยะระหว่าง: อนุญาตและไม่ควรทำ :)
Alok บันทึก

16
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)

constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error

constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok

// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";

lcharp[0] = 'X';      // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error

// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X';          // compile error
((char*)astr)[0] = 'X'; // ok

1
ตัวชี้ของคุณไม่ชี้ไปที่ "ตัวอักษรสตริงคงที่" ตามคำถาม
caf

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

10

ในกรณีที่ทั้งคุณสามารถแก้ไขตัวอักษรสตริงโดยไม่คำนึงว่าตัวชี้ไปยังตัวอักษรสตริงที่ถูกประกาศเป็นหรือchar *const char *

อย่างไรก็ตามความแตกต่างคือถ้าตัวชี้เป็นconst char *คอมไพเลอร์จะต้องให้การวินิจฉัยถ้าคุณพยายามที่จะแก้ไขค่าชี้ไปที่ แต่ถ้าตัวชี้เป็นchar *แล้วมันไม่ได้


1
"ไม่ว่าในกรณีใดคุณสามารถปรับเปลี่ยนสตริงตัวอักษรโดยไม่คำนึงว่า ... [มัน] ถูกประกาศเป็น char * หรือ const char *" ฉันยอมรับว่าโปรแกรมเมอร์ไม่ควรลอง แต่คุณจะบอกว่าคอมไพเลอร์ C ทุกคนในทุก ๆ แพลตฟอร์มจะปฏิเสธรหัสจัดให้รหัสล้มเหลวในขณะใช้งานหรืออย่างอื่น ผมเชื่อว่าหนึ่งไฟล์อาจมีความหมายและ initialisation และไฟล์อื่นอาจจะมีและมีการextern ... name *name = 'X';ใน 'ระบบปฏิบัติการที่เหมาะสม' ซึ่งอาจล้มเหลว แต่สำหรับระบบฝังตัวฉันคาดหวังให้ทำบางสิ่งบางอย่างเฉพาะแพลตฟอร์ม / คอมไพเลอร์
gbulmer

@gbulmer: คุณไม่สามารถแก้ไขตัวอักษรสตริงในโปรแกรม C ที่ถูกต้อง สิ่งที่โปรแกรม C ที่ไม่ถูกต้องซึ่งพยายามทำอาจไม่อยู่ที่นี่หรือที่นั่น
caf

@gbulmer: คำจำกัดความที่มีประโยชน์คือโปรแกรมที่ไม่ทำลายข้อ จำกัด ใด ๆ ที่ระบุโดยมาตรฐานภาษา C กล่าวอีกนัยหนึ่งโปรแกรมที่ปรับเปลี่ยนสตริงตัวอักษรนั้นไม่ถูกต้องในลักษณะเดียวกับที่ตัวชี้ null หรือทำการหารด้วย 0 ไม่ถูกต้อง
caf

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

1
เมื่อมาตรฐานไม่สามารถยืนยันอะไรอย่างใดฉันคิดว่าการกำหนดพฤติกรรมว่า 'ไม่ได้กำหนด' ดูเหมือนจะเป็นขอบเขตที่ถูกต้องและเป็นประโยชน์ เพื่อยืนยันความสัมพันธ์ 'โปรแกรม C ที่ถูกต้อง' ' ไม่สามารถยกเลิกการอ้างอิงตัวชี้โมฆะ' เสียงเทียบเท่ากับการพิสูจน์ปัญหาการหยุดชะงัก แต่ฉันไม่รังเกียจ ฉันจะไม่ทำมันและคาดหวังว่าจะได้มันไปด้วย 'สก็อตฟรี' :-)
gbulmer

4

กรณีที่ 1:

char *str = "Hello";
str[0] = 'M'  //Warning may be issued by compiler, and will cause segmentation fault upon running the programme

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

กรณีที่ 2:

const char *str = "Hello";
str[0] = 'M'  //Compile time error

กรณีที่ 3:

char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".

2

ครั้งแรกที่คุณสามารถเปลี่ยนแปลงได้จริงถ้าคุณต้องการที่สองคุณไม่สามารถ อ่านเกี่ยวกับconstความถูกต้อง (มีคำแนะนำดีๆเกี่ยวกับความแตกต่าง) นอกจากนี้ยังมีchar const * nameที่ที่คุณไม่สามารถสั่งการได้


สิ่งที่สามารถเปลี่ยนแปลงได้?
Antti Haapala

2

คำถามคือสิ่งที่แตกต่างกัน

char *name

ซึ่งชี้ไปที่ตัวอักษรสตริงคงที่และ

const char *cname

คือให้

char *name = "foo";

และ

const char *cname = "foo";

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

ความแตกต่างที่สำคัญของ 2 โดยทั่วไปก็คือว่า*cnameหรือcname[n]จะมีการประเมินเพื่อ lvalues ชนิดconst charในขณะที่*nameหรือname[n]จะมีการประเมินเพื่อ lvalues ประเภทcharซึ่งเป็นlvalues แก้ไขได้ คอมไพเลอร์ที่สอดคล้องจะต้องสร้างข้อความการวินิจฉัยหากเป้าหมายของการมอบหมายไม่ใช่ lvalue ที่แก้ไขได้ ; ไม่จำเป็นต้องมีคำเตือนใด ๆ เกี่ยวกับการกำหนดค่า lvalues ​​ประเภทchar:

name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostics message

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


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