Phần còn lại


585

Cái nào tốt hơn để sử dụng trong số các câu dưới đây trong C?

static const int var = 5;

hoặc là

#define var 5

hoặc là

enum { var = 5 };

35
Thật thú vị, đây gần như chính xác là câu hỏi tương tự như stackoverflow.com/questions/1637332/static-const-vs-define . Sự khác biệt duy nhất là câu hỏi đó là về C ++, và câu hỏi này là về C. Vì câu trả lời của tôi là C ++ cụ thể, tôi nói điều đó làm cho chúng không giống nhau, nhưng những người khác có thể không đồng ý.
TED

53
Không đồng nhất, chắc chắn. Có rất nhiều lĩnh vực trong đó C ++ cho phép cú pháp C vì lý do tương thích. Trong những trường hợp đó, những câu hỏi như "cách tốt nhất để làm X" sẽ có những câu trả lời khác nhau trong C ++. Ví dụ khởi tạo đối tượng.
MSalters


Làm thế nào đây không phải là ý kiến ​​dựa? Mỗi người có một mục đích khác nhau
Sam Hammamy

1
@RobertSsupportsMonicaCellio, Có. Cảm ơn bạn đã thân mật
Vijay

Câu trả lời:


690

Nó phụ thuộc vào những gì bạn cần giá trị cho. Bạn (và mọi người khác cho đến nay) đã bỏ qua phương án thứ ba:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Bỏ qua các vấn đề về việc lựa chọn tên, sau đó:

  • Nếu bạn cần vượt qua một con trỏ xung quanh, bạn phải sử dụng (1).
  • Vì (2) rõ ràng là một tùy chọn, bạn không cần phải chuyển con trỏ xung quanh.
  • Cả (1) và (3) đều có một biểu tượng trong bảng biểu tượng của trình gỡ lỗi - giúp việc gỡ lỗi dễ dàng hơn. Nhiều khả năng (2) sẽ không có biểu tượng, khiến bạn tự hỏi nó là gì.
  • (1) không thể được sử dụng làm thứ nguyên cho các mảng ở phạm vi toàn cầu; cả (2) và (3) có thể.
  • (1) không thể được sử dụng làm thứ nguyên cho các mảng tĩnh ở phạm vi chức năng; cả (2) và (3) có thể.
  • Theo C99, tất cả những thứ này có thể được sử dụng cho các mảng cục bộ. Về mặt kỹ thuật, sử dụng (1) sẽ ngụ ý việc sử dụng VLA (mảng có độ dài thay đổi), mặc dù kích thước được tham chiếu bởi 'var' tất nhiên sẽ được cố định ở kích thước 5.
  • (1) không thể được sử dụng ở những nơi như báo cáo chuyển đổi; cả (2) và (3) có thể.
  • (1) không thể được sử dụng để khởi tạo các biến tĩnh; cả (2) và (3) có thể.
  • (2) có thể thay đổi mã mà bạn không muốn thay đổi vì nó được sử dụng bởi bộ tiền xử lý; cả (1) và (3) sẽ không có tác dụng phụ không mong muốn như thế.
  • Bạn có thể phát hiện xem (2) đã được đặt trong bộ tiền xử lý chưa; không (1) cũng không (3) cho phép điều đó.

Vì vậy, trong hầu hết các bối cảnh, hãy thích 'enum' hơn các lựa chọn thay thế. Mặt khác, các gạch đầu tiên và cuối cùng có thể là các yếu tố kiểm soát - và bạn phải suy nghĩ nhiều hơn nếu bạn cần thỏa mãn cả hai cùng một lúc.

Nếu bạn hỏi về C ++, thì bạn sẽ sử dụng tùy chọn (1) - hằng số tĩnh - mọi lúc.


111
danh sách tuyệt vời! Một nhược điểm enumlà chúng được triển khai như int([C99] 6.7.2.2/3). Một #definephép bạn chỉ định unsigned và dài với ULhậu tố, và constcho phép bạn đưa ra một loại. enumcó thể gây ra vấn đề với chuyển đổi loại thông thường.
Gauthier

37
(2) mọi người LUÔN phàn nàn về loại an toàn. Tôi không bao giờ hiểu tại sao không chỉ sử dụng "#define var ((int) 5)" và giúp bạn có được sự an toàn với một định nghĩa.
Ingo Blackman

6
@RedX: bạn sẽ phải ở trong một môi trường rất đặc biệt để không gian phải lo lắng. Điều đó nói rằng, không phải enumcũng không #definesử dụng thêm không gian, mỗi se. Giá trị sẽ xuất hiện trong mã đối tượng như một phần của hướng dẫn thay vì được phân bổ lưu trữ trong phân đoạn dữ liệu hoặc trong heap hoặc trên ngăn xếp. Bạn sẽ có một số không gian được phân bổ cho static const int, nhưng trình biên dịch có thể tối ưu hóa nó nếu bạn không lấy địa chỉ.
Jonathan Leffler

15
Một 'phiếu' khác cho enums (và static const): chúng không thể thay đổi. a definecó thể là #undefine'd trong đó một enumstatic constđược cố định với giá trị đã cho.
Daan Timmer

15
@QED: Không, cảm ơn bạn. Một hằng số đơn giản là an toàn bên ngoài dấu ngoặc đơn. Hoặc, chỉ cho tôi cách một chương trình có thể được dự kiến ​​biên dịch hợp pháp sẽ bị thay đổi bằng cách không có 5 trong ngoặc đơn. Nếu đó là đối số cho macro kiểu hàm hoặc nếu có bất kỳ toán tử nào trong biểu thức, thì bạn sẽ đúng khi đổ lỗi cho tôi nếu tôi không bao gồm dấu ngoặc đơn. Nhưng đó không phải là trường hợp ở đây.
Jonathan Leffler

282

Nói chung:

static const

Bởi vì nó tôn trọng phạm vi và là loại an toàn.

Thông báo trước duy nhất tôi có thể thấy: nếu bạn muốn biến có thể được xác định trên dòng lệnh. Vẫn còn một sự thay thế:

#ifdef VAR // Very bad name, not long enough, too general, etc..
  static int const var = VAR;
#else
  static int const var = 5; // default value
#endif

Bất cứ khi nào có thể, thay vì macro / dấu chấm lửng, hãy sử dụng phương án thay thế an toàn.

Nếu bạn thực sự CẦN đi với một macro (ví dụ: bạn muốn __FILE__hoặc __LINE__), thì bạn nên đặt tên cho macro của mình RẤT cẩn thận: trong quy ước đặt tên của nó, Boost đề xuất tất cả chữ hoa, bắt đầu bằng tên của dự án (ở đây BOOST_ ), trong khi lướt qua thư viện, bạn sẽ nhận thấy đây là (nói chung) theo sau là tên của khu vực cụ thể (thư viện) sau đó với một tên có ý nghĩa.

Nó thường làm cho tên dài :)


2
Đồng ý - cũng với #define, có một mối nguy hiểm chung về mã xáo trộn vì bộ tiền xử lý không biết về cú pháp.
NeilDurant

10
Sử dụng #if tốt hơn #ifdef, nhưng nếu không thì tôi đồng ý. +1.
Tim Post

58
Đây là truyền giáo C ++ tiêu chuẩn. Câu trả lời dưới đây là rõ ràng hơn trong việc giải thích các tùy chọn thực sự là gì và có ý nghĩa gì. Cụ thể: Tôi vừa gặp sự cố với "const const". Ai đó đã sử dụng nó để định nghĩa khoảng 2000 "hằng số" trong một tệp tiêu đề. Sau đó, tệp tiêu đề này được bao gồm trong khoảng 100 tệp ".c" và ".cpp". => 8Mbyte cho "hằng số". Tuyệt quá. Có tôi biết rằng bạn có thể sử dụng một trình liên kết để loại bỏ các hằng số không được ước tính, nhưng sau đó điều này vẫn để lại cho bạn các "hằng số" được tham chiếu. Chạy ra khỏi không gian tất cả những gì sai với câu trả lời này.
Ingo Blackman

2
@IngoBlackman: Với một trình biên dịch tốt, chỉ những người staticcó địa chỉ được lấy vẫn còn; và nếu địa chỉ được lấy, người ta không thể sử dụng một #definehoặc enum(không có địa chỉ) ... vì vậy tôi thực sự không biết cách nào có thể được sử dụng. Nếu bạn có thể loại bỏ "đánh giá thời gian biên dịch", extern constthay vào đó bạn có thể tìm kiếm .
Matthieu M.

15
@Tim Post: #ifcó thể thích hợp hơn #ifdefcho các cờ boolean, nhưng trong trường hợp này, nó sẽ làm cho không thể định nghĩa varnhư 0từ dòng lệnh. Vì vậy, trong trường hợp này, #ifdefcó ý nghĩa hơn, miễn 0là giá trị pháp lý cho var.
Maarten

108

Trong C, cụ thể? Trong C, câu trả lời đúng là: use #define(hoặc, nếu thích hợp, enum)

Mặc dù có các thuộc tính phạm vi và gõ của một constđối tượng, nhưng trong constcác đối tượng thực tế trong C (trái ngược với C ++) không phải là hằng số thực và do đó thường vô dụng trong hầu hết các trường hợp thực tế.

Vì vậy, trong C, sự lựa chọn nên được xác định bằng cách bạn dự định sử dụng hằng số của mình. Ví dụ: bạn không thể sử dụng một const intđối tượng làm casenhãn (trong khi macro sẽ hoạt động). Bạn không thể sử dụng một const intđối tượng làm độ rộng trường bit (trong khi macro sẽ hoạt động). Trong C89 / 90, bạn không thể sử dụng một constđối tượng để chỉ định kích thước mảng (trong khi macro sẽ hoạt động). Ngay cả trong C99, bạn không thể sử dụng một constđối tượng để chỉ định kích thước mảng khi bạn cần một mảng không phải là VLA .

Nếu điều này quan trọng với bạn thì nó sẽ quyết định lựa chọn của bạn. Hầu hết thời gian, bạn sẽ không có lựa chọn nào khác ngoài sử dụng #definetrong C. Và đừng quên một lựa chọn khác, đó là tạo ra các hằng số thực sự trong C - enum.

Trong C ++, constcác đối tượng là các hằng thực sự, vì vậy trong C ++, hầu như luôn luôn tốt hơn để thích constbiến thể ( staticmặc dù không cần rõ ràng trong C ++).


6
"bạn không thể sử dụng một đối tượng const int làm nhãn trường hợp (trong khi macro sẽ hoạt động)" ---> Về tuyên bố này tôi đã kiểm tra một biến const int trong C trong trường hợp chuyển đổi nó đang hoạt động ....
john

8
@john: Chà, bạn cần cung cấp mã mà bạn đã kiểm tra và đặt tên cho trình biên dịch cụ thể. Sử dụng const intcác đối tượng trong trường hợp nhãn là bất hợp pháp trong tất cả các phiên bản của ngôn ngữ C. (Tất nhiên, trình biên dịch của bạn có thể hỗ trợ miễn phí dưới dạng C ++ - như phần mở rộng ngôn ngữ.)
AnT

11
"... và do đó thường vô dụng trong hầu hết các trường hợp thực tế ." Tôi không đồng ý. Chúng hoàn toàn hữu ích miễn là bạn không cần sử dụng tên như một biểu thức không đổi. Từ "hằng" trong C có nghĩa là một cái gì đó có thể được đánh giá tại thời điểm biên dịch; constcó nghĩa là chỉ đọc. const int r = rand();là hoàn toàn hợp pháp.
Keith Thompson

Trong c ++, tốt hơn là sử dụng constexprso với constđặc biệt với các stlthùng chứa như arrayhoặc bitset.
Mayukh Sarkar

1
@john bạn phải thử nghiệm trong switch()tuyên bố, không phải trong casemột. Tôi cũng vừa bị bắt gặp cái này
Hi-Angel

32

Sự khác biệt giữa static const#definelà cái trước sử dụng bộ nhớ và cái sau không sử dụng bộ nhớ để lưu trữ. Thứ hai, bạn không thể chuyển địa chỉ của a #definetrong khi bạn có thể chuyển địa chỉ của a static const. Trên thực tế, nó phụ thuộc vào hoàn cảnh nào chúng ta phải chịu, chúng ta cần chọn một trong hai trường hợp này. Cả hai đều tốt nhất trong những hoàn cảnh khác nhau. Xin đừng cho rằng cái này tốt hơn cái kia ... :-)

Nếu đó là trường hợp, Dennis Ritchie sẽ giữ người tốt nhất một mình ... hahaha ... :-)


6
1 đề cập đến bộ nhớ, một số hệ thống nhúng vẫn không có nhiều, mặc dù tôi có lẽ sẽ bắt đầu sử dụng consts tĩnh và chỉ chuyển sang # định nghĩa nếu cần thiết.
fluffyben

3
Tôi chỉ thử nó. Thật vậy, const int sử dụng bộ nhớ bổ sung so với #define hoặc enum. Vì chúng tôi lập trình các hệ thống nhúng, chúng tôi không thể đủ khả năng sử dụng bộ nhớ bổ sung. Vì vậy, chúng tôi sẽ quay lại sử dụng #define hoặc enum.
Davide Andrea

2
Thực tế mà nói không đúng (nữa) rằng a constkhông sử dụng bộ nhớ. GCC (được thử nghiệm với 4.5.3 và một vài phiên bản mới hơn) dễ dàng tối ưu hóa const intthành một chữ trực tiếp trong mã của bạn khi sử dụng -O3. Vì vậy, nếu bạn thực hiện phát triển nhúng RAM thấp (ví dụ: AVR), bạn có thể sử dụng C const một cách an toàn nếu bạn sử dụng GCC hoặc trình biên dịch tương thích khác. Tôi chưa thử nó nhưng mong Clang sẽ làm điều tương tự btw.
Raphael

19

Trong C #definelà phổ biến hơn nhiều. Bạn có thể sử dụng các giá trị đó để khai báo kích thước mảng chẳng hạn:

#define MAXLEN 5

void foo(void) {
   int bar[MAXLEN];
}

ANSI C không cho phép bạn sử dụng static consts trong bối cảnh này theo như tôi biết. Trong C ++, bạn nên tránh các macro trong những trường hợp này. Bạn có thể viết

const int maxlen = 5;

void foo() {
   int bar[maxlen];
}

và thậm chí bỏ qua staticvì liên kết nội bộ được ngụ ý bởi const[chỉ trong C ++].


1
Bạn có ý nghĩa gì với "liên kết nội bộ"? Tôi có thể có const int MY_CONSTANT = 5;trong một tập tin và truy cập nó trong một tập tin extern const int MY_CONSTANT;khác. Tôi không thể tìm thấy bất kỳ thông tin nào trong tiêu chuẩn (ít nhất là C99) về constviệc thay đổi hành vi mặc định "6.2.2: 5 Nếu việc khai báo một định danh cho một đối tượng có phạm vi và không có thông số lớp lưu trữ, thì liên kết của nó là bên ngoài".
Gauthier

@Gauthier: Xin lỗi, về điều đó. Tôi nên nói "được ngụ ý bởi const đã có trong ngôn ngữ C ++". Điều này là cụ thể cho C ++.
sellibitze

@sellibitze nó tốt đẹp để xem một số tranh cãi trên đường đi thay vì tấn Ý KIẾN Nếu có sẽ thưởng cho lập luận đúng, bạn đã nhận nó!
Paul

1
Kể từ C99, đoạn mã thứ hai của bạn là hợp pháp. barlà một VLA (mảng có chiều dài thay đổi); trình biên dịch có khả năng tạo mã như thể độ dài của nó không đổi.
Keith Thompson

14

Một nhược điểm khác của constC là bạn không thể sử dụng giá trị trong việc khởi tạo một giá trị khác const.

static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;

// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND 
                                     * NUMBER_OF_HANDS;

Thậm chí điều này không làm việc với một const kể từ khi biên dịch không xem nó như là một hằng số:

static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!

Tôi rất vui khi sử dụng đánh máy consttrong những trường hợp này, nếu không ...


5
Một chút muộn để trò chơi, nhưng câu hỏi này đến trong một câu hỏi khác. Theo đuổi lý do tại sao static uint8_t const ARRAY_SIZE = 16;tất cả của bạn đột nhiên không còn biên dịch có thể là một chút thách thức, đặc biệt là khi #define ARRAY_SIZE 256bị chôn vùi mười lớp sâu trong một mạng lưới các tiêu đề rối. Đó là tất cả các tên mũ ARRAY_SIZEđang yêu cầu rắc rối. Dự trữ ALL_CAPS cho các macro và không bao giờ xác định một macro không ở dạng ALL_CAPS.
David Hammen

@David: lời khuyên âm thanh, mà tôi sẽ làm theo.
Gauthier

1
4 năm sau bạn đã tiết kiệm cho tôi rất nhiều thời gian để tìm ra lý do tại sao tôi không thể "làm tổ" const. Điều này có thể được nâng cao hơn nữa!
Plouff

11

Nếu bạn có thể nhận được ngay với nó, static constcó rất nhiều lợi thế. Nó tuân theo các nguyên tắc phạm vi bình thường, có thể nhìn thấy trong trình gỡ lỗi và thường tuân theo các quy tắc mà các biến tuân theo.

Tuy nhiên, ít nhất là trong tiêu chuẩn C ban đầu, nó không thực sự là một hằng số. Nếu bạn sử dụng #define var 5, bạn có thể viết int foo[var];dưới dạng khai báo, nhưng bạn không thể làm điều đó (ngoại trừ phần mở rộng trình biên dịch "với static const int var = 5;. Đây không phải là trường hợp trong C ++, nơi static constphiên bản có thể được sử dụng ở bất cứ đâu #definephiên bản có thể, và tôi tin điều này cũng là trường hợp với C99.

Tuy nhiên, không bao giờ đặt tên một #definehằng với tên viết thường. Nó sẽ ghi đè lên bất kỳ khả năng sử dụng tên đó cho đến khi kết thúc các đơn vị dịch. Các hằng số vĩ mô phải nằm trong không gian tên riêng của chúng, theo truyền thống là tất cả các chữ in hoa, có lẽ có tiền tố.


6
Thật không may, đây không phải là trường hợp của C99. consttrong C99 vẫn không phải là một hằng số thực sự. Bạn có thể khai báo kích thước mảng bằng constC99, nhưng chỉ vì C99 hỗ trợ Mảng độ dài biến. Vì lý do này, nó sẽ chỉ hoạt động khi VLAs được phép. Ví dụ: ngay cả trong C99, bạn vẫn không thể sử dụng a constđể khai báo kích thước của mảng thành viên trong a struct.
AnT

Mặc dù đúng là C99 sẽ không cho phép bạn làm điều đó, GCC (được thử nghiệm với 4.5.3) sẽ hoàn toàn cho phép bạn khởi tạo các mảng với const intkích thước như thể đó là một C ++ const hoặc macro. Cho dù bạn muốn phụ thuộc vào độ lệch của GCC so với tiêu chuẩn tất nhiên là lựa chọn của bạn, tôi sẽ tự mình đi theo trừ khi bạn thực sự có thể sử dụng trình biên dịch khác ngoài GCC hoặc Clang, sau này có tính năng tương tự ở đây (đã thử nghiệm với Clang 3.7).
Raphael

7

Đó là LUÔN LUÔN nên sử dụng const, thay vì #define. Đó là vì const được xử lý bởi trình biên dịch và #define bởi bộ tiền xử lý. Nó giống như #define tự nó không phải là một phần của mã (nói đại khái).

Thí dụ:

#define PI 3.1416

Tên PI biểu tượng có thể không bao giờ được nhìn thấy bởi trình biên dịch; nó có thể được gỡ bỏ bằng vi xử lý trước khi mã nguồn thậm chí đạt đến một trình biên dịch. Do đó, tên PI có thể không được nhập vào bảng ký hiệu. Điều này có thể gây nhầm lẫn nếu bạn gặp lỗi trong quá trình biên dịch liên quan đến việc sử dụng hằng số, bởi vì thông báo lỗi có thể tham khảo 3.1416, không phải PI. Nếu PI được xác định trong tệp tiêu đề bạn không viết, bạn sẽ không biết 3.1416 đó đến từ đâu.

Vấn đề này cũng có thể xuất hiện trong trình gỡ lỗi tượng trưng, ​​bởi vì, một lần nữa, tên bạn đang lập trình có thể không có trong bảng ký hiệu.

Giải pháp:

const double PI = 3.1416; //or static const...

6

#define var 5sẽ gây rắc rối cho bạn nếu bạn có những thứ như thế mystruct.var.

Ví dụ,

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

Bộ tiền xử lý sẽ thay thế nó và mã sẽ không được biên dịch. Vì lý do này, phong cách mã hóa truyền thống đề nghị tất cả các hằng số #definesử dụng chữ in hoa để tránh xung đột.


6

Tôi đã viết chương trình thử nghiệm nhanh để chứng minh một sự khác biệt:

#include <stdio.h>

enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};

#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32

int main(int argc, char *argv[]) {

   printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);

   return(0);
}

Điều này biên dịch với các lỗi và cảnh báo:

main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
      ^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
      ^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
        ^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
        ^

Lưu ý rằng enum đưa ra lỗi khi định nghĩa đưa ra cảnh báo.


4

Định nghĩa

const int const_value = 5;

không phải lúc nào cũng xác định một giá trị không đổi. Một số trình biên dịch (ví dụ tcc 0.9.26 ) chỉ phân bổ bộ nhớ được xác định với tên "const_value". Sử dụng mã định danh "const_value", bạn không thể sửa đổi bộ nhớ này. Nhưng bạn vẫn có thể sửa đổi bộ nhớ bằng cách sử dụng một định danh khác:

const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.

Điều này có nghĩa là định nghĩa

#define CONST_VALUE 5

là cách duy nhất để xác định một giá trị không đổi mà không thể sửa đổi bằng bất kỳ phương tiện nào.


8
Sửa đổi một giá trị không đổi bằng cách sử dụng một con trỏ là hành vi không xác định. Nếu bạn sẵn sàng đến đó, #definecũng có thể được sửa đổi, bằng cách chỉnh sửa mã máy.
ugoren

Bạn đúng một phần. Tôi đã thử mã với Visual Studio 2012 và nó được in 5. Nhưng người ta không thể thay đổi #definevì đó là một vĩ mô tiền xử lý. Nó không tồn tại trong chương trình nhị phân. Nếu một người muốn sửa đổi tất cả những nơi CONST_VALUEđược sử dụng, người ta phải làm từng cái một.
dùng2229691

3
@ugoren: Giả sử bạn viết #define CONST 5, sau đó if (CONST == 5) { do_this(); } else { do_that(); }, và trình biên dịch loại bỏ elsenhánh. Làm thế nào để bạn đề xuất chỉnh sửa mã máy để thay đổi CONSTthành 6?
Keith Thompson

@KeithThndry, tôi chưa bao giờ nói nó có thể được thực hiện dễ dàng và đáng tin cậy. Chỉ #definelà nó không chống đạn.
ugoren

3
@ugoren: Quan điểm của tôi là "chỉnh sửa mã máy" không phải là cách hợp lý để nhân đôi hiệu ứng thay đổi giá trị của a #define. Cách thực sự duy nhất để làm điều đó là chỉnh sửa mã nguồn và biên dịch lại.
Keith Thompson

4

Mặc dù câu hỏi là về số nguyên, nhưng đáng chú ý là #define và enums là vô dụng nếu bạn cần một cấu trúc hoặc chuỗi không đổi. Cả hai thường được truyền cho các chức năng như con trỏ. (Với các chuỗi cần thiết; với các cấu trúc, nó hiệu quả hơn nhiều.)

Đối với số nguyên, nếu bạn đang ở trong một môi trường nhúng với bộ nhớ rất hạn chế, bạn có thể cần phải lo lắng về nơi lưu trữ hằng số và cách truy cập vào nó. Trình biên dịch có thể thêm hai hằng số trong thời gian chạy, nhưng thêm hai #defines vào thời gian biên dịch. Hằng số #define có thể được chuyển đổi thành một hoặc nhiều lệnh MOV [ngay lập tức], có nghĩa là hằng số được lưu trữ hiệu quả trong bộ nhớ chương trình. Một hằng số const sẽ được lưu trữ trong phần .const trong bộ nhớ dữ liệu. Trong các hệ thống có kiến ​​trúc Harvard, có thể có sự khác biệt về hiệu suất và mức sử dụng bộ nhớ, mặc dù chúng có thể nhỏ. Chúng có thể quan trọng đối với tối ưu hóa lõi cứng của các vòng bên trong.


3

Đừng nghĩ rằng có một câu trả lời cho "cái nào luôn tốt nhất" nhưng, như Matthieu nói

static const

là loại an toàn. #defineMặc dù vậy, thú cưng lớn nhất của tôi là khi gỡ lỗi trong Visual Studio, bạn không thể xem biến. Nó đưa ra một lỗi mà biểu tượng không thể được tìm thấy.


1
"Bạn không thể xem biến" Phải, đó không phải là biến. Nó không thay đổi, tại sao bạn cần xem nó? Bạn có thể tìm thấy ở khắp mọi nơi nó được sử dụng chỉ đơn giản bằng cách tìm kiếm nhãn. Tại sao bạn cần (hoặc thậm chí muốn) để xem #define?
Marshall Eubanks

3

Ngẫu nhiên, một giải pháp thay thế #define, cung cấp phạm vi phù hợp nhưng hoạt động như một hằng số "thực", là "enum". Ví dụ:

enum {number_ten = 10;}

Trong nhiều trường hợp, thật hữu ích khi xác định các kiểu liệt kê và tạo các biến của các loại đó; nếu điều đó được thực hiện, trình gỡ lỗi có thể hiển thị các biến theo tên liệt kê của chúng.

Tuy nhiên, một cảnh báo quan trọng khi thực hiện điều đó: trong C ++, các kiểu liệt kê có khả năng tương thích hạn chế với các số nguyên. Ví dụ, theo mặc định, người ta không thể thực hiện số học theo chúng. Tôi thấy rằng đó là một hành vi mặc định tò mò cho enums; Mặc dù thật tuyệt khi có loại "enum nghiêm ngặt", với mong muốn có C ++ thường tương thích với C, tôi nghĩ rằng hành vi mặc định của loại "enum" nên có thể hoán đổi cho nhau với số nguyên.


1
Trong C, hằng số liệt kê luôn có kiểu int, vì vậy "enum hack" không thể được sử dụng với các kiểu số nguyên khác. ( Loại liệt kê tương thích với một số loại số nguyên do triển khai, không nhất thiết int, nhưng trong trường hợp này, loại này là ẩn danh nên không thành vấn đề.)
Keith Thompson

@KeithThndry: Vì tôi đã viết ở trên, tôi đã đọc rằng MISRA-C sẽ squawk nếu trình biên dịch gán một loại khác với intbiến được liệt kê (mà trình biên dịch được phép thực hiện) và người ta cố gắng gán cho biến đó một thành viên của liệt kê riêng của mình. Tôi muốn các ủy ban tiêu chuẩn sẽ thêm các cách khai báo các kiểu số nguyên với ngữ nghĩa được chỉ định. BẤT K platform nền tảng nào, bất kể charkích thước, có thể ví dụ như khai báo một loại sẽ bao bọc mod 65536, ngay cả khi trình biên dịch phải thêm nhiều AND R0,#0xFFFFhướng dẫn hoặc tương đương.
supercat

Bạn có thể sử dụng uint16_t, mặc dù tất nhiên đó không phải là một kiểu liệt kê. Sẽ thật tuyệt khi cho phép người dùng chỉ định loại số nguyên được sử dụng để biểu thị một kiểu liệt kê nhất định, nhưng bạn có thể đạt được nhiều hiệu ứng tương tự với một typedeffor uint16_tvà một chuỗi #defines cho các giá trị riêng lẻ.
Keith Thompson

1
@KeithThndry: Tôi hiểu rằng vì lý do lịch sử, chúng tôi bị mắc kẹt với thực tế là một số nền tảng sẽ đánh giá 2U < -1Llà đúng và những nền tảng khác là sai và hiện tại chúng tôi bị mắc kẹt với thực tế là một số nền tảng sẽ thực hiện so sánh giữa uint32_tint32_tnhư đã ký và một số là không dấu, nhưng điều đó không có nghĩa là Ủy ban không thể định nghĩa một người kế thừa tương thích hướng lên với C bao gồm các loại có ngữ nghĩa sẽ phù hợp với tất cả các trình biên dịch.
supercat

1

Một sự khác biệt đơn giản:

Lúc trước chế biến, hằng số được thay thế bằng giá trị của nó. Vì vậy, bạn không thể áp dụng toán tử dereference cho một định nghĩa, nhưng bạn có thể áp dụng toán tử dereference cho một biến.

Như bạn cho rằng, định nghĩa là const tĩnh nhanh hơn.

Ví dụ: có:

#define mymax 100

bạn không thể làm printf("address of constant is %p",&mymax);.

nhưng có

const int mymax_var=100

bạn có thể làm printf("address of constant is %p",&mymax_var);.

Để rõ ràng hơn, định nghĩa được thay thế bằng giá trị của nó ở giai đoạn tiền xử lý, vì vậy chúng tôi không có bất kỳ biến nào được lưu trữ trong chương trình. Chúng tôi chỉ có mã từ đoạn văn bản của chương trình nơi định nghĩa được sử dụng.

Tuy nhiên, đối với const tĩnh, chúng ta có một biến được phân bổ ở đâu đó. Đối với gcc, const tĩnh được phân bổ trong đoạn văn bản của chương trình.

Ở trên, tôi muốn nói về toán tử tham chiếu để thay thế sự bổ nhiệm bằng tham chiếu.


1
Câu trả lời của bạn rất sai. Đây là về C, câu trả lời của bạn liên quan đến C ++, có ngữ nghĩa rất khác nhau cho constvòng loại. C không có hằng số Symbolica ngoài hằng số enum . A const intlà một biến. Bạn cũng nhầm lẫn ngôn ngữ và thực hiện cụ thể. Không có yêu cầu nơi đặt đối tượng. Và nó thậm chí không đúng với gcc: thông thường nó đặt constcác biến đủ điều kiện trong .rodataphần. Nhưng điều đó phụ thuộc vào nền tảng mục tiêu. Và bạn có nghĩa là địa chỉ của nhà điều hành &.
quá trung thực cho trang web này vào

0

Chúng tôi đã xem xét mã trình biên dịch được sản xuất trên MBF16X ... Cả hai biến thể đều dẫn đến cùng một mã cho các hoạt động số học (ví dụ THÊM ngay lập tức).

Vì vậy, const intđược ưa thích cho các loại kiểm tra trong khi #definelà phong cách cũ. Có lẽ nó là trình biên dịch cụ thể. Vì vậy, kiểm tra mã lắp ráp sản xuất của bạn.


-1

Tôi không chắc chắn nếu tôi đúng, nhưng trong sự kêu gọi quan điểm của tôi #define giá trị d nhanh hơn nhiều so với việc gọi bất kỳ biến thông thường nào khác (hoặc giá trị const). Đó là bởi vì khi chương trình đang chạy và nó cần sử dụng một số biến được khai báo thông thường, nó cần phải nhảy đến vị trí chính xác trong bộ nhớ để lấy biến đó.

Ngược lại khi nó sử dụng #definegiá trị d, chương trình không cần phải chuyển sang bất kỳ bộ nhớ được phân bổ nào, nó chỉ lấy giá trị. Nếu #define myValue 7và chương trình gọi myValue, nó hoạt động giống hệt như khi nó chỉ gọi 7.

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.