Khi nào và vì mục đích gì nên sử dụng từ khóa const trong C cho các biến?


58

Trong khi nhận được mã của tôi được xem xét ở đây , vấn đề sử dụng consttừ khóa đã xuất hiện. Tôi hiểu rằng nó được sử dụng để thực hiện hành vi chỉ đọc trên các biến.

Tôi bối rối về những tình huống khác nhau khi nó có thể hữu ích.

  • Nó có nên được sử dụng cho mục đích rõ ràng trong các nguyên mẫu chức năng?
  • Có nên sử dụng nó như một biện pháp bảo mật trong quá trình phát triển mã?
  • Có nên sử dụng nó trong phạm vi của các hàm khác nhau để khai báo các hằng số thời gian chạy không?
  • Có nên sử dụng nó không?

Những câu hỏi này chỉ là ví dụ về sự nhầm lẫn mà tôi đang phải đối mặt. Sự nhầm lẫn chung là

  • Khi nào nên là consttừ khóa được sử dụng trong lập trình C?
  • Các loại lợi ích khác nhau có thể đạt được bằng cách sử dụng từ khóa này trong C là gì?
  • Có bất kỳ khuyết điểm của việc sử dụng consttừ khóa?


Nó đã được chỉ ra rằng câu hỏi này có thể quá rộng do tất cả những câu hỏi này trong chi tiết câu hỏi của tôi. Tôi chỉ muốn làm rõ rằng những câu hỏi này chỉ là để làm rõ sự nhầm lẫn liên quan đến câu hỏi chính.

Khi nào và vì mục đích gì nên sử dụng từ khóa const trong C cho các biến?

Nó cũng có thể được rephras như

Việc sử dụng đúng consttừ khóa trong C` với những ưu và nhược điểm giống nhau.


Đối với những người bỏ phiếu, nó sẽ bị đóng, vui lòng giải thích lý do tại sao nó không thuộc danh mục Specific issues with software development. Tôi đang khá cụ thể.
Aseem Bansal

Tôi đoán là do bạn đã hỏi nhiều câu hỏi trong cùng một bài, và do đó, câu hỏi của bạn thuộc danh mục Quá rộng: "Có quá nhiều câu trả lời có thể, hoặc câu trả lời tốt sẽ quá dài cho định dạng này. Vui lòng thêm chi tiết để thu hẹp tập hợp câu trả lời hoặc cô lập một vấn đề có thể được trả lời trong một vài đoạn. "
Robert Harvey

1
@RobertHarvey Tất cả những câu hỏi này chỉ để giải thích cho sự nhầm lẫn của tôi. Câu hỏi duy nhất của tôi vẫn là tiêu đề của câu hỏi này. Một câu trả lời tốt cho điều đó sẽ bao gồm tất cả các câu hỏi liên quan. Vậy nó vẫn là a specific issue. Việc sử dụng đúng consttừ khóa trong C` với những ưu và nhược điểm giống nhau.
Aseem Bansal

@RobertHarvey Bây giờ có tốt hơn không?
Aseem Bansal

Câu trả lời:


64

Khi xem lại mã, tôi áp dụng các quy tắc sau:

  • Luôn sử dụng constcho các tham số chức năng được truyền bởi tham chiếu trong đó chức năng không sửa đổi (hoặc miễn phí) dữ liệu được trỏ đến.

    int find(const int *data, size_t size, int value);
  • Luôn sử dụng constcho các hằng số có thể được xác định bằng cách sử dụng #define hoặc enum. Trình biên dịch có thể định vị dữ liệu trong bộ nhớ chỉ đọc (ROM) do kết quả (mặc dù trình liên kết thường là một công cụ tốt hơn cho mục đích này trong các hệ thống nhúng).

    const double PI = 3.14;
  • Không bao giờ sử dụng const trong một nguyên mẫu hàm cho một tham số được truyền bởi giá trị . Nó không có ý nghĩa và do đó chỉ là 'tiếng ồn'.

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, const int value); 
  • Khi thích hợp, sử dụng const volatiletrên các vị trí không thể thay đổi bởi chương trình nhưng vẫn có thể thay đổi. Các thanh ghi phần cứng là trường hợp sử dụng điển hình ở đây, ví dụ một thanh ghi trạng thái phản ánh trạng thái thiết bị:

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;

Sử dụng khác là tùy chọn. Ví dụ, các tham số cho một chức năng trong quá trình thực hiện chức năng có thể được đánh dấu là const.

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

hoặc các giá trị trả về hàm hoặc các phép tính thu được và sau đó không bao giờ thay đổi:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

Những cách sử dụng này constchỉ cho thấy rằng bạn sẽ không thay đổi biến; họ không thay đổi cách thức hoặc nơi lưu trữ biến. Trình biên dịch tất nhiên có thể tìm ra rằng một biến không bị thay đổi, nhưng bằng cách thêm constbạn cho phép nó thực thi điều đó. Điều này có thể giúp người đọc và thêm một số an toàn (mặc dù nếu các chức năng của bạn đủ lớn hoặc phức tạp đến mức điều này tạo ra sự khác biệt lớn, bạn có thể có vấn đề khác). Chỉnh sửa - ví dụ. một hàm dày đặc 200 dòng với các vòng lặp lồng nhau và nhiều tên biến dài hoặc tương tự nhau, biết rằng các biến nhất định không bao giờ thay đổi có thể dễ dàng hiểu rõ hơn. Các chức năng như vậy đã được thiết kế xấu hoặc duy trì.


Vấn đề với const. Bạn có thể sẽ nghe thấy thuật ngữ "ngộ độc const". Điều này xảy ra khi thêm constvào một tham số chức năng khiến 'constness' lan truyền.

Chỉnh sửa - const độc: ví dụ trong chức năng:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

nếu chúng ta thay đổi strthành const, chúng ta phải đảm bảo rằng fuction_bcũng mất một const. Và như vậy nếu function_bvượt qua strvào function_c, vv Như bạn có thể tưởng tượng này có thể là đau đớn nếu nó truyền thành nhiều file riêng biệt / modules. Nếu nó truyền vào một chức năng không thể thay đổi (ví dụ: thư viện hệ thống), thì việc truyền diễn viên trở nên cần thiết. Vì vậy, rắc constxung quanh trong mã hiện tại có lẽ là yêu cầu rắc rối. Trong mã mới, tốt nhất là constđủ điều kiện nhất quán khi thích hợp.

Vấn đề khó hiểu hơn constlà nó không có trong ngôn ngữ gốc. Là một tiện ích bổ sung, nó không phù hợp lắm. Để bắt đầu, nó có hai ý nghĩa (như trong các quy tắc ở trên, có nghĩa là "Tôi sẽ không thay đổi điều này" và "điều này không thể sửa đổi"). Nhưng hơn thế, nó có thể nguy hiểm. Ví dụ, biên dịch và chạy mã này và (tùy thuộc vào trình biên dịch / tùy chọn), nó có thể bị sập khi chạy:

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchrtrả về char*không a const char*. Vì tham số cuộc gọi của nó là constnó phải truyền tham số cuộc gọi đến char*. Và trong trường hợp này, bỏ đi tài sản lưu trữ chỉ đọc thực sự. Chỉnh sửa: - điều này thường áp dụng cho các vars trong bộ nhớ chỉ đọc. Theo 'ROM', ý tôi không chỉ là ROM vật lý mà là bất kỳ bộ nhớ nào được bảo vệ chống ghi, như xảy ra với phần mã của các chương trình chạy trên một HĐH thông thường.

Nhiều hàm thư viện chuẩn hoạt động theo cùng một cách, vì vậy hãy cẩn thận: khi bạn có các hằng số thực (nghĩa là được lưu trữ trong ROM), bạn phải rất cẩn thận để không bị mất hằng số.


Nó không thực sự có hai ý nghĩa. Nó chỉ có nghĩa, như bạn nói, " Tôi sẽ không thay đổi điều này." Ví dụ, nó hoàn toàn hợp lệ để có một const volatilebiến.
gièm pha

2
Điều đó đúng với các tham số hàm nhưng đối với các hằng số ở phạm vi tệp, ví dụ, một constbiến thực sự chỉ đọc: constnói "Điều này không thể sửa đổi". Một const volatilemặt khác nói: "Đây không thể được sửa đổi, nhưng nó có thể thay đổi". Tôi sẽ thêm một đề cập đến các chất bay hơi vào câu trả lời của tôi.
William Morris

Tôi không đồng ý với nhận xét của bạn, chỉ với ý tưởng rằng đó là một hướng dẫn cho trình biên dịch để đưa nó vào ROM. Điều đó ngụ ý một số loại bảo vệ thời gian chạy chống lại hành vi không xác định (nghĩa là sửa đổi một const thông qua một cách nào đó), điều này có thể dẫn đến những hiểu lầm như "tại sao tôi có thể thay đổi constbiến này thông qua một constcon trỏ" (một câu hỏi phổ biến trên SO và mọi diễn đàn C khác !). Nhìn chung, tôi thích câu trả lời này :)
ghê tởm

Bạn đã đúng, "Đặt cái này vào ROM" là không chính xác (mặc dù cô đọng :-) - Tôi sẽ thay đổi nó thành "Điều này không thể sửa đổi", điều này chính xác hơn. Cảm ơn ý kiến ​​của bạn.
William Morris

Đây là một tổng quan tuyệt vời. Tôi muốn diễn đạt nó consttuyên bố cả dữ liệu chỉ đọc và chế độ xem chỉ đọc của dữ liệu. Sự khác biệt là điều này không thể được sửa đổi so với điều mà bạn không thể sửa đổi điều này.
Jon Purdy

8

Nói chung trong bất kỳ ngôn ngữ lập trình nào, nên sử dụng consthoặc sửa đổi tương đương

  • Nó có thể làm rõ với người gọi rằng những gì họ truyền vào sẽ không thay đổi
  • Cải thiện tốc độ tiềm năng vì trình biên dịch biết chắc chắn nó có thể bỏ qua một số thứ chỉ liên quan nếu tham số có thể thay đổi
  • Bảo vệ bạn vô tình thay đổi giá trị

1

Vâng, về cơ bản đó là câu trả lời của TheLQ.

Là một biện pháp bảo mật cho lập trình viên, do đó bạn không sửa đổi một biến và không gọi các hàm có thể sửa đổi chúng. Trong một mảng hoặc cấu trúc, bộ xác định const chỉ ra rằng các giá trị của nội dung của chúng sẽ không được sửa đổi và thậm chí trình biên dịch sẽ không cho phép bạn làm như vậy. Tuy nhiên, bạn vẫn có thể dễ dàng thay đổi giá trị của biến chỉ bằng cách sử dụng.

Trong những gì tôi thường thấy, nó chủ yếu được sử dụng để thêm các giá trị không đổi trong mã và để chỉ ra rằng mảng hoặc cấu trúc sẽ không được sửa đổi nếu bạn gọi một hàm cụ thể. Phần cuối cùng này rất quan trọng, vì khi bạn gọi một hàm SILL sửa đổi mảng hoặc cấu trúc của bạn, bạn có thể muốn giữ phiên bản gốc, vì vậy bạn tạo một bản sao của biến và sau đó chuyển nó sang hàm. Nếu đó không phải là trường hợp, rõ ràng bạn không yêu cầu bản sao, vì vậy, ví dụ bạn có thể thay đổi,

int foo(Structure s);

đến

int foo(const Structure * s);

và không nhận được bản sao trên đầu.

Chỉ cần thêm, lưu ý rằng C có các quy tắc cụ thể với bộ xác định const. Ví dụ,

int b = 1;
const int * a = &b;

không giống như

int b = 1;
int * const a = &b;

Mã đầu tiên sẽ không cho phép bạn sửa đổi a. Trong trường hợp thứ hai, con trỏ không đổi nhưng nội dung của nó thì không, vì vậy trình biên dịch sẽ cho phép bạn nói * a = 3;mà không có lỗi trình biên dịch, nhưng bạn không athể tham chiếu đến điều khác.


0

Đồng ý với tuyên bố của TheLQ:

Khi làm việc với một nhóm lập trình viên tuyên bố constlà một cách tốt để chỉ ra rằng biến nói không nên được sửa đổi, hoặc chỉ để nhắc nhở bản thân trong các dự án lớn. Theo nghĩa đó, nó hữu ích và có thể cứu nhiều người đau đầu.

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.