"static const" vs "# define " vs " enum"

Który z poniższych wyrażeń w C jest lepszy?

static const int var = 5;

Lub

#define var 5

Lub

enum { var = 5 };
 490
Author: Housy, 2009-11-04

17 answers

Ogólnie rzecz biorąc:

static const

Ponieważ szanuje zakres i jest bezpieczny dla typu.

Jedyne zastrzeżenie, jakie widziałem: jeśli chcesz, aby zmienna była ewentualnie zdefiniowana w wierszu poleceń. Istnieje jeszcze alternatywa:

#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

W miarę możliwości, zamiast makr / elipsy, użyj alternatywy bezpiecznej dla typu.

Jeśli naprawdę potrzebujesz użyć makra( na przykład chcesz __FILE__ lub __LINE__), to lepiej nazwij swoje makro bardzo ostrożnie: w nazwie zjazd Boost zaleca wszystkie wielkie litery, zaczynając od nazwy projektu( tutaj BOOST_), podczas przeglądania biblioteki zauważysz, że jest to (ogólnie) po nazwie konkretnego obszaru (biblioteka), a następnie ze znaczącą nazwą.

To generalnie sprawia, że długie nazwy :)

 258
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 09:09:48

To zależy od tego, do czego potrzebujesz wartości. Ty (i wszyscy do tej pory) pominąłeś trzecią alternatywę:

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

Ignorując problemy z wyborem nazwy, to:

  • Jeśli chcesz przekazać wskaźnik, musisz użyć (1).
  • ponieważ (2) jest widoczną opcją, nie musisz podawać wskaźników.
  • zarówno (1), jak i (3) mają symbol w tabeli symboli debuggera - co ułatwia debugowanie. Informatyka jest bardziej prawdopodobne, że (2) nie będzie miał symbolu, pozostawiając zastanawiasz się, co to jest.
  • (1) nie może być używany jako wymiar dla tablic o zasięgu globalnym; zarówno (2), jak i (3) mogą.
  • (1)nie może być używany jako wymiar tablic statycznych w zakresie funkcji; zarówno (2), jak i (3) mogą.
  • pod C99, wszystkie z nich mogą być używane dla lokalnych tablic. Technicznie, użycie (1) implikowałoby użycie Vla (tablicy o zmiennej długości), chociaż wymiar, do którego odwołuje się 'var', byłby oczywiście ustawiony na rozmiar 5.
  • (1) nie może być używany w miejscach takich jak polecenia switch; zarówno (2), jak i (3) mogą.
  • (1) nie może być użyty do inicjalizacji zmiennych statycznych; zarówno (2), jak i (3) mogą.
  • (2) może zmienić kod, którego nie chcesz zmienić, ponieważ jest on używany przez preprocesor; zarówno (1), jak i (3) nie będą miały takich nieoczekiwanych efektów ubocznych.
  • można wykryć, czy (2) zostało ustawione w preprocesorze; ani (1), ani (3) na to nie pozwalają.

Więc w większości kontekstów preferuj 'enum' nad alternatywami. W przeciwnym razie, pierwszy i ostatni punkt może być czynnikiem kontrolującym - i trzeba myśleć mocniej, jeśli trzeba zaspokoić oba na raz.

Jeśli pytałeś o C++, to używałeś opcji (1) - statyczny const-za każdym razem.

 589
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-04 18:22:11

W C, konkretnie? W C prawidłowa odpowiedź brzmi: użyj #define (lub, jeśli właściwe, enum)

Chociaż korzystne jest posiadanie właściwości zakresu i typowania obiektu const, w rzeczywistości const obiekty w C (w przeciwieństwie do C++) nie są prawdziwymi stałymi i dlatego są zwykle bezużyteczne w większości praktycznych przypadków.

Więc w C wybór powinien być określony przez sposób, w jaki planujesz użyć swojej stałej. Na przykład nie można używać obiektu const int jako etykiety case (podczas gdy makro będzie pracy). Nie można używać obiektu const int jako pola bitowego (podczas gdy makro będzie działać). W C89 / 90 nie można użyć obiektu const do określenia rozmiaru tablicy(podczas gdy makro będzie działać). Nawet w C99 nie możesz użyć obiektu const do określenia rozmiaru tablicy, gdy potrzebujesz tablicy innej niżVla.

Jeśli jest to dla Ciebie ważne, to zadecyduje o Twoim wyborze. Przez większość czasu nie będziesz miał wyboru, jak tylko użyć #define W C. i nie zapomnij o innej alternatywie, która tworzy prawdziwe stałe w C - enum.

W C++ const obiekty są prawdziwymi stałymi, więc w C++ prawie zawsze lepiej jest preferować wariant const (nie ma potrzeby jawnego static W C++).

 94
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 09:16:39

Różnica między static const i #define polega na tym, że pierwszy używa pamięci, a późniejszy nie używa pamięci do przechowywania. Po drugie, nie można przekazać adresu #define, podczas gdy można przekazać adres static const. Właściwie to w zależności od tego, w jakich okolicznościach jesteśmy, musimy wybrać jedną z tych dwóch. Oba są w najlepszym wydaniu w różnych okolicznościach. Proszę nie zakładać, że jeden jest lepszy od drugiego... :-)

Gdyby tak było, Dennis Ritchie trzymałby najlepszego w spokoju... hahaha... :-)

 28
Author: wrapperm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 09:30:06

W C #define jest znacznie bardziej popularny. Możesz użyć tych wartości do deklarowania rozmiarów tablic, na przykład:

#define MAXLEN 5

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

ANSI C nie pozwala na użycie static const s w tym kontekście, o ile wiem. W C++ należy unikać makr w takich przypadkach. Możesz napisać

const int maxlen = 5;

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

I nawet pominąć static, ponieważ wewnętrzne powiązanie jest implikowane przez const już [tylko w C++].

 14
Author: sellibitze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-21 21:06:11

Kolejną wadą const W C jest to, że nie można użyć wartości przy inicjalizacji innego 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;

Nawet to nie działa z const, ponieważ kompilator nie widzi jej jako stałej:

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!

Chętnie użyłbym w takich przypadkach napisu const, w przeciwnym razie...

 13
Author: Gauthier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-24 13:32:02

Jeśli uda Ci się to ujść na sucho, static const ma wiele zalet. Jest on zgodny z normalnymi zasadami zakresu, jest widoczny w debuggerze i generalnie przestrzega reguł, które są przestrzegane przez zmienne.

Jednakże, przynajmniej w oryginalnym standardzie C, nie jest to stała. Jeśli używasz #define var 5, możesz napisać int foo[var]; jako deklarację, ale nie możesz tego zrobić (z wyjątkiem rozszerzenia kompilatora" z static const int var = 5;. Nie jest tak w C++, gdzie Wersja static const może być używana wszędzie tam, gdzie wersja #define może, I wierzę, że tak jest również w przypadku C99.

Nigdy jednak nie podawaj stałej #define z małą literą. Nadpisuje wszelkie możliwe użycie tej nazwy aż do końca jednostki tłumaczeniowej. Stałe makro powinny znajdować się w swojej własnej przestrzeni nazw, którą tradycyjnie są wszystkie duże litery, być może z prefiksem.

 9
Author: David Thornley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-04 14:38:24

Napisałem szybki program testowy, aby zademonstrować jedną różnicę:

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

To zestawia z tymi błędami i ostrzeżeniami:

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
        ^

Zauważ, że enum wyświetla błąd, gdy define wyświetla ostrzeżenie.

 6
Author: Michael Potter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-29 17:37:40

#define var 5 sprawi ci kłopoty, jeśli masz takie rzeczy jak mystruct.var.

Na przykład,

struct mystruct {
    int var;
};

#define var 5

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

Preprocesor go zastąpi, a kod nie zostanie skompilowany. Z tego powodu tradycyjny styl kodowania sugeruje, że wszystkie stałe #define s używają dużych liter, aby uniknąć konfliktu.

 5
Author: Non-maskable Interrupt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 09:33:26

Zawsze lepiej jest użyć const, zamiast # define. Dzieje się tak dlatego, że const jest traktowany przez kompilator, a #define przez preprocesor. To tak, jakby #define nie było częścią kodu (z grubsza rzecz biorąc).

Przykład:

#define PI 3.1416

Symboliczna nazwa PI może nigdy nie być widziana przez kompilatory; może zostać usunięta przez preprocesor, zanim kod źródłowy dotrze nawet do kompilatora. W rezultacie nazwa PI może nie zostać wprowadzona do tabeli symboli. Może to być mylące, jeśli otrzymasz błąd podczas kompilacji polegający na użyciu stałej, ponieważ komunikat o błędzie może odnosić się do 3.1416, a nie do PI. Gdyby PI było zdefiniowane w pliku nagłówkowym, którego nie napisałeś, nie miałbyś pojęcia, skąd pochodzi ten 3.1416.

Ten problem może pojawić się również w symbolicznym debugerze, ponieważ nazwa, z którą programujesz, może nie znajdować się w tabeli symboli.

Rozwiązanie:

const double PI = 3.1416; //or static const...
 5
Author: suren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-01 17:48:29

Definicja

const int const_value = 5;

Nie zawsze definiuje stałą wartość. Niektóre Kompilatory (na przykład tcc 0.9.26) po prostu przydzielają pamięć identyfikowaną nazwą "const_value". Używając identyfikatora "const_value" nie można modyfikować tej pamięci. Ale nadal można modyfikować pamięć za pomocą innego identyfikatora:

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.

Oznacza to definicję

#define CONST_VALUE 5

Jest jedynym sposobem na zdefiniowanie stałej wartości, której nie można w żaden sposób modyfikować.

 4
Author: user2229691,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-01 17:44:02

Nie myśl, że jest odpowiedź na "co jest zawsze najlepsze", ale, jak powiedział Matthieu

static const

Jest bezpieczny. Moim największym zmartwieniem z #define jest to, że podczas debugowania w Visual Studio nie można oglądać zmiennej. To daje błąd, że symbol nie może zostać znaleziony.

 3
Author: Afcrowe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 09:11:19

Nawiasem mówiąc, alternatywą dla #define, która zapewnia właściwy zasięg, ale zachowuje się jak" prawdziwa "stała, jest "enum". Na przykład:

enum {number_ten = 10;}

W wielu przypadkach przydatne jest definiowanie typów wyliczeniowych i tworzenie zmiennych tych typów; jeśli tak się stanie, debuggery mogą wyświetlać zmienne zgodnie z ich nazwą wyliczenia.

Jedno ważne zastrzeżenie: w C++ Typy wyliczane mają ograniczoną kompatybilność z liczbami całkowitymi. Na przykład, domyślnie, jeden nie można na nich wykonywać arytmetyki. Uważam, że jest to ciekawe domyślne zachowanie dla enum; chociaż byłoby miło mieć typ "strict enum", biorąc pod uwagę pragnienie, aby C++ był ogólnie kompatybilny z C, myślę, że domyślne zachowanie typu" enum " powinno być wymienne z liczbami całkowitymi.

 3
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 09:32:14

Chociaż pytanie dotyczyło liczb całkowitych, warto zauważyć, że # define i enums są bezużyteczne, jeśli potrzebujesz stałej struktury lub ciągu. Są one zwykle przekazywane do funkcji jako wskaźniki. (W przypadku ciągów jest to wymagane; w przypadku struktur jest to znacznie wydajniejsze.)

Jeśli chodzi o liczby całkowite, jeśli znajdujesz się w środowisku wbudowanym z bardzo ograniczoną pamięcią, możesz potrzebować martwić się o to, gdzie jest przechowywana stała i jak są kompilowane dostępy do niej. Kompilator może dodać dwa const w czas wykonania, ale dodaj dwa # definiuje w czasie kompilacji. Stała # define może być zamieniona na jedną lub więcej instrukcji MOV [immediate], co oznacza, że stała jest skutecznie przechowywana w pamięci programu. Stała const będzie przechowywana wsekcja const w pamięci danych. W systemach z architekturą Harvarda mogą występować różnice w wydajności i zużyciu pamięci, chociaż prawdopodobnie są one niewielkie. Mogą mieć znaczenie dla hard-core optymalizacji pętli wewnętrznych.

 2
Author: Adam Haun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-05 21:17:24

Prosta różnica:

W czasie wstępnego przetwarzania stała jest zastępowana jej wartością. Nie można więc zastosować operatora dereferencji do zdefiniowania, ale można zastosować operatora dereferencji do zmiennej.

Jak można by przypuszczać, definiowanie jest szybsze niż statyczne const.

Na przykład:

#define mymax 100

Nie możesz tego zrobić printf("address of constant is %p",&mymax);.

Ale mając

const int mymax_var=100

Możesz to zrobić printf("address of constant is %p",&mymax_var);.

Aby było bardziej jasne, define jest zastępowany przez jego wartość w etapie wstępnego przetwarzania, więc nie mamy żadnej zmiennej zapisanej w programie. Mamy tylko kod z segmentu tekstowego programu, w którym użyto define.

Jednak dla statycznego const mamy zmienną, która jest gdzieś przydzielana. Dla gcc, statyczne const są przydzielane w segmencie tekstowym programu.

Powyżej chciałem opowiedzieć o operatorze referencyjnym, więc zastąp dereferencję referencją.

 1
Author: mihaitzateo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-01 17:47:12

Przyjrzeliśmy się wyprodukowanemu kodowi asemblera na mbf16x... Oba warianty dają ten sam kod dla operacji arytmetycznych (np. ADD Immediate).

Więc const int jest preferowane do sprawdzenia typu, podczas gdy #define to stary styl. Może jest specyficzny dla kompilatora. Więc sprawdź swój wyprodukowany kod asemblera.

 0
Author: guest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-01 17:49:24

Nie jestem pewien, czy mam rację, ale moim zdaniem wywołanie wartości #defineD jest znacznie szybsze niż wywołanie jakiejkolwiek innej normalnie deklarowanej zmiennej (lub wartości const). Dzieje się tak dlatego, że gdy program jest uruchomiony i musi użyć jakiejś normalnie zadeklarowanej zmiennej, musi przeskoczyć do dokładnego miejsca w pamięci, aby uzyskać tę zmienną.

W przeciwnym wypadku, gdy używa wartości #defined, program nie musi przeskakiwać do żadnej przydzielonej pamięci, po prostu pobiera wartość. Jeśli #define myValue 7 i wywołanie programu myValue, zachowuje się dokładnie tak samo jak wtedy, gdy po prostu wywołuje 7.

 0
Author: pajczur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-05 19:59:49