Sự khác biệt giữa char * và const char *?


176

Sự khác biệt giữa

char* name

trỏ đến một chuỗi ký tự không đổi và

const char* name

bạn có ý nghĩa gì bởi " chuỗi ký tự không đổi " trong C (không phải C ++)
gbulmer 23/03 '

1
... tên char * có thể được tạo để trỏ đến một chuỗi ký tự không đổi
Iceman

hằng số trong "chuỗi ký tự không đổi" là dư thừa, vì tất cả các chuỗi ký tự trong các thực thể hằng số lý thuyết. Đó là nội dung của biến có thể được tạo thành hằng số hoặc có thể thay đổi. Khai báo "const" đơn giản sẽ đưa ra lỗi thời gian biên dịch nếu bạn cố gắng thay đổi nội dung của ký tự được chỉ bởi "tên"
Cupcake

Đơn giản: tên "char * name" là một con trỏ tới char, tức là cả hai có thể được thay đổi ở đây. Tên "const char * name" là một con trỏ tới const char tức là con trỏ có thể thay đổi nhưng không phải là char.
akD

Đọc những điều này từ phải sang trái.
Trương Đức Trương

Câu trả lời:


405

char*là một con trỏ có thể thay đổi thành một ký tự / chuỗi có thể thay đổi .

const char*là một con trỏ có thể thay đổi thành một ký tự / chuỗi bất biến . Bạn không thể thay đổi nội dung của (các) vị trí mà con trỏ này trỏ tới. Ngoài ra, trình biên dịch được yêu cầu đưa ra thông báo lỗi khi bạn cố gắng làm như vậy. Vì lý do tương tự, chuyển đổi từ const char *sang char*bị phản đối.

char* constlà một con trỏ bất biến (nó không thể trỏ đến bất kỳ vị trí nào khác) nhưng nội dung của vị trí mà điểm đó có thể thay đổi .

const char* constlà một con trỏ bất biến cho một ký tự / chuỗi bất biến .


4
Nhầm lẫn có thể được làm sáng tỏ với việc sử dụng một biến sau các tuyên bố được đề cập ở trên và bằng cách đưa ra tham chiếu đến biến đó.
ankit.karwasra

3
@ ankit.karwasra, Bạn đã bỏ lỡ một lần nữa:char const *
Pacerier

Tôi giả sử hai tùy chọn có ký tự / chuỗi có thể thay đổi là rất nguy hiểm, vì bạn có thể thực hiện bộ nhớ lỗi phân tách và nếu bạn thực sự thông minh, bạn có thể hack máy tính. Đó là lý do tại sao trình biên dịch luôn hiển thị các cảnh báo trong các triển khai mà tôi nghĩ
Daniel N.

1
Sẽ không đột biến char *đưa ra lỗi phân khúc trong khi chạy?
Divyanshu Maithani

1
Vì vậy, tôi sử dụng constnếu tôi muốn trình biên dịch báo lỗi nếu tôi quên và thay đổi dữ liệu do nhầm lẫn, phải không?
Kế toán م

43
char *name

Bạn có thể thay đổi char thành nameđiểm nào, và cũng là char mà nó trỏ tới.

const char* name

Bạn có thể thay đổi char thành nameđiểm nào, nhưng bạn không thể sửa đổi char tại điểm đó.
sửa lỗi: Bạn có thể thay đổi con trỏ, nhưng không thay đổi thành char name( https://msdn.microsoft.com/en-us/l Library / vstudio / whkd4k6a ( v = vs.100) .aspx , xem "Ví dụ" ). Trong trường hợp này, constspecifier áp dụng cho char, không phải dấu hoa thị.

Theo trang MSDN và http://en.cppreference.com/w/cpp/lingu/declarations , phần consttrước *là một phần của trình tự xác định từ chối, trong khi phần constsau *là một phần của trình khai báo.
Một trình xác định khai báo có thể được theo sau bởi nhiều khai báo, đó là lý do tại sao const char * c1, c2khai báo c1as const char *c2as const char.

BIÊN TẬP:

Từ các bình luận, câu hỏi của bạn dường như đang hỏi về sự khác biệt giữa hai khai báo khi con trỏ trỏ đến một chuỗi bằng chữ.

Trong trường hợp đó, bạn không nên sửa đổi char thành nameđiểm nào, vì nó có thể dẫn đến Hành vi không xác định . Chuỗi ký tự có thể được phân bổ trong các vùng bộ nhớ chỉ đọc (xác định thực hiện) và một chương trình người dùng không nên sửa đổi nó trong bất kỳ cách nào. Bất kỳ nỗ lực để làm như vậy đều dẫn đến Hành vi không xác định.

Vì vậy, sự khác biệt duy nhất trong trường hợp đó (sử dụng với chuỗi ký tự) là khai báo thứ hai mang lại cho bạn một lợi thế nhỏ. Trình biên dịch thường sẽ đưa ra cảnh báo trong trường hợp bạn cố gắng sửa đổi chuỗi ký tự trong trường hợp thứ hai.

Ví dụ mẫu trực tuyến:

#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;
}

Đầu ra:

cc1: cảnh báo đang được coi là lỗi
prog.c: Trong hàm 'main':
prog.c: 9: error: truyền đối số 1 của 'strcpy' loại bỏ vòng loại khỏi loại mục tiêu con trỏ

Lưu ý trình biên dịch cảnh báo cho trường hợp thứ hai nhưng không phải cho trường hợp thứ nhất.


Cảm ơn .. tôi đã trộn với chuỗi ký tự không đổi, được định nghĩa là: char * name = "String Literal"; Thay đổi "Chuỗi ký tự" là không xác định ..
Iceman

@ user1279782: Ơ, đợi đã! Bạn đang nói về các điểm chỉ vào chuỗi ký tự ở đây? Trong trường hợp đó, bạn không nên sửa đổi char mà các nameđiểm trong cả hai trường hợp. Nó có thể dẫn đến UB.
Alok Lưu

Vâng, đó là điểm. Vì vậy, trong trường hợp đó char * name và const char * name hoạt động tương tự nhau, phải không?
Iceman

4
Câu trả lời này là vô cùng mơ hồ hoặc chỉ đơn giản là sai. Tôi sẽ giải thích "Bạn không thể thay đổi char thành điểm tên nào, nhưng Bạn có thể sửa đổi char mà nó trỏ tới." Vì không thể tự sửa đổi con trỏ, nhưng có thể sửa đổi vị trí bộ nhớ mà nó trỏ đến, không chính xác: ideone.com/6lUY9s thay thế cho C: ideone.com/x3PcTP
che giấu

1
@shroudednight: Bạn cần tìm hiểu thêm một chút về các hành vi Không xác định và cần phân biệt giữa: được phép và không nên thực hiện. :)
Alok Lưu

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
Không có con trỏ nào của bạn trỏ đến "chuỗi ký tự không đổi" theo câu hỏi.
phê

Đáng lưu ý rằng việc thay đổi char *giá trị gây ra lỗi phân đoạn vì chúng tôi đang cố gắng sửa đổi một chuỗi ký tự (hiện diện trong bộ nhớ chỉ đọc)
Divyanshu Maithani

10

Trong cả hai trường hợp, bạn không thể sửa đổi một chuỗi ký tự, bất kể con trỏ tới chuỗi ký tự đó được khai báo là char *hay const char *.

Tuy nhiên, sự khác biệt là nếu con trỏ là const char *trình biên dịch phải đưa ra chẩn đoán nếu bạn cố gắng sửa đổi giá trị trỏ, nhưng nếu con trỏ là char *vậy thì không.


1
"Trong cả hai trường hợp, bạn không thể sửa đổi một chuỗi ký tự, bất kể ... [nó] được khai báo là char * hay const char *" Tôi đồng ý rằng lập trình viên không nên thử, nhưng bạn có nói rằng mọi trình biên dịch C, trên mọi nền tảng sẽ từ chối mã, sắp xếp cho mã bị lỗi trong thời gian chạy, hoặc cái gì khác? Tôi tin rằng, một tệp có thể có định nghĩa và khởi tạo, và một tệp khác có thể chứa extern ... namevà có *name = 'X';. Trên 'hệ điều hành phù hợp', điều đó có thể thất bại, nhưng trên các hệ thống nhúng, tôi hy vọng nó sẽ làm một cái gì đó cụ thể cho nền tảng / trình biên dịch.
xe cứu thương

@gbulmer: Bạn không thể sửa đổi một chuỗi ký tự trong một chương trình C chính xác. Thật là một chương trình C không chính xác mà cố gắng có thể dẫn đến không có ở đây cũng không có.
phê

@gbulmer: Một định nghĩa hữu ích là một chương trình không phá vỡ bất kỳ ràng buộc nào được chỉ định bởi tiêu chuẩn ngôn ngữ C. Nói cách khác, một chương trình sửa đổi một chuỗi ký tự là không chính xác giống như một chương trình hủy bỏ một con trỏ null hoặc thực hiện phép chia cho 0 là không chính xác.
phê

caf - tôi nghĩ đó có thể là những gì bạn muốn nói Sau đó, "Trong cả hai trường hợp, bạn không thể sửa đổi một chuỗi ký tự" dường như quá mức cho biết nó. Sẽ là chính xác khi nói "Trong cả hai trường hợp, các ràng buộc được chỉ định bởi tiêu chuẩn ngôn ngữ C đã bị phá vỡ, bất kể .... Trình biên dịch hoặc hệ thống thời gian chạy không thể xác định các vi phạm của tiêu chuẩn trong mọi trường hợp." Tôi giả sử tiêu chuẩn có vị trí mà hiệu ứng không xác định?
xe cứu thương

1
Khi một tiêu chuẩn không thể khẳng định bất cứ điều gì, tôi nghĩ việc xác định hành vi là 'không xác định' dường như là chính xác ranh giới đúng và hữu ích. Để khẳng định mối quan hệ, 'chương trình C chính xác' ' không thể hủy bỏ con trỏ null' nghe có vẻ tương đương với việc chứng minh vấn đề tạm dừng. Nhưng tôi không phiền. Tôi sẽ không làm điều đó và hy vọng sẽ thoát khỏi nó 'scott free' :-)
xe đạp

4

TRƯỜNG HỢP 1:

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

Các tập hợp ở trên str để trỏ đến giá trị bằng chữ "Xin chào" được mã hóa cứng trong hình ảnh nhị phân của chương trình, được gắn cờ là chỉ đọc trong bộ nhớ, có nghĩa là bất kỳ thay đổi nào trong chuỗi ký tự này là bất hợp pháp và điều đó sẽ gây ra lỗi phân đoạn.

TRƯỜNG HỢP 2:

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

TRƯỜNG HỢP 3:

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

2

Cái đầu tiên bạn thực sự có thể thay đổi nếu bạn muốn, cái thứ hai bạn không thể. Đọc về consttính chính xác (có một số hướng dẫn tốt đẹp về sự khác biệt). Cũng có char const * namenơi bạn không thể từ chối nó.


Chính xác những gì có thể thay đổi?
Antti Haapala

2

Câu hỏi là sự khác biệt giữa

char *name

trỏ đến một chuỗi ký tự không đổi và

const char *cname

Tức là đã cho

char *name = "foo";

const char *cname = "foo";

Không có nhiều sự khác biệt giữa 2 và cả hai có thể được xem là chính xác. Do di sản dài của mã C, các chuỗi ký tự chuỗi đã có một loại char[], không const char[], và có rất nhiều mã cũ hơn chấp nhận char *thay vì const char *, ngay cả khi chúng không sửa đổi các đối số.

Sự khác biệt chính của 2 nói chung là *cnamehoặc cname[n]sẽ đánh giá các giá trị của loại const char, trong khi *namehoặc name[n]sẽ đánh giá các giá trị của loại char, đó là các giá trị có thể sửa đổi . Một trình biên dịch tuân thủ được yêu cầu để tạo ra một thông điệp chẩn đoán nếu mục tiêu của bài tập không phải là một giá trị có thể sửa đổi ; nó không cần tạo ra bất kỳ cảnh báo nào về việc gán giá trị loại char:

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

Trình biên dịch không bắt buộc phải dừng quá trình biên dịch trong cả hai trường hợp; nó là đủ để nó tạo ra một cảnh báo cho nhiệm vụ cname[0]. Chương trình kết quả không phải là một chương trình chính xác . Hành vi của các cấu trúc là không xác định . Nó có thể bị sập, hoặc thậm chí tệ hơn, nó có thể không gặp sự cố và có thể thay đổi chuỗi ký tự trong bộ nhớ.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.