2010-02-22 9 views
42

W C, czy preferuję stałe ponad definicje? Ostatnio czytałem dużo kodu, a wszystkie przykłady w dużym stopniu wykorzystują definicje.Czy mam preferować stałe ponad definicje?

+17

Uważaj na zespół sztokholmski! – Potatoswatter

+0

[Ta odpowiedź] (http://stackoverflow.com/a/1674459/2235567) jest trafna. –

+0

możliwy duplikat ["static const" vs "#define" w C] (http://stackoverflow.com/questions/1674032/static-const-vs-define-in-c) –

Odpowiedz

92

Nie, ogólnie rzecz biorąc, nie powinieneś używać stałych obiektów w C do tworzenia stałych nazw. Aby utworzyć stałą o nazwie o stałej wartości w C, należy użyć dowolnego z makr (#define) lub wyliczeń. W rzeczywistości język C nie ma stałych, w sensie, który wydaje się sugerować.(C jest znacząco różne od C++ w tym zakresie)

w języku C pojęć stałe i stałej ekspresji są określone bardzo różny od C++. W C stała oznacza wartość literalną , podobną do 123. Oto kilka przykładów stałych w C

123 
34.58 
'x' 

Stałe w C mogą być wykorzystane do budowy wyrażeń stałych. Ponieważ jednak obiekty o stałych kwalifikacjach dowolnego typu nie są stałymi stałymi w C, nie można ich używać w wyrażeniach stałych, a w konsekwencji nie można używać obiektów o ustalonej wartości, w których wymagane są wyrażenia stałe.

Na przykład, następujące nie jest stały

const int C = 123; /* C is not a constant!!! */ 

i od powyżej C nie jest stały, to nie może być wykorzystane do deklarowania typu tablicy w zakresie plików

typedef int TArray[C]; /* ERROR: constant expression required */ 

ramę nie może być używany jako etykieta na etykiecie

switch (i) { 
    case C: ; /* ERROR: constant expression required */ 
} 

Nie może być używany jako bit-fi szerokość ELD

struct S { 
    int f : C; /* ERROR: constant expression required */ 
}; 

To nie może być stosowany jako inicjator dla obiektu z czasu przechowywania static

static int i = C; /* ERROR: constant expression required */ 

To nie może być stosowany jako inicjator enum

enum { 
    E = C /* ERROR: constant expression required */ 
}; 

czyli nie może być stosowany wszędzie tam, gdzie wymagana jest stała stała.

Może to wydawać się sprzeczne z intuicją, ale w ten sposób zdefiniowany jest język C.

Oto dlaczego widzisz te liczne #define -s w kodzie, z którym pracujesz. Ponownie, w języku C obiekt o stałym zakrecie ma bardzo ograniczone zastosowanie. Zasadniczo są całkowicie bezużyteczne jako "stałe", dlatego w języku C zasadniczo zmuszeni jesteście używać #define lub wyliczeń, aby zadeklarować prawdziwe stałe.

Oczywiście w sytuacjach, w których obiekt o ustalonej wartości działa dla Ciebie, tzn. Robi to, co chcesz, to jest on lepszy od makr na wiele sposobów, ponieważ ma zasięg i typ. Powinieneś raczej preferować takie obiekty w stosownych przypadkach, jednak w ogólnym przypadku musisz wziąć pod uwagę powyższe ograniczenia.

+16

+1 dla zrozumienia i prawidłowego artykułowania różnic C/C++, punktu, który wydaje się być utracony na wielu ludziach. – asveikau

+1

Dobra odpowiedź. Dziękuję za zaznaczenie problemu ze zmiennymi "const" dla deklarowania rozmiarów macierzy. –

+2

+1 Wow, świetna odpowiedź – helpermethod

9

Stałe powinny być preferowane od define s. Istnieje kilka zalet:

  • Typ bezpieczeństwa. Podczas gdy język C jest słabo typowany w wersji zwartej, użycie funkcji define powoduje utratę całego bezpieczeństwa typu, co pozwala kompilatorowi wykryć problemy.

  • Łatwość debugowania. Możesz zmienić wartość stałych przez debugger, podczas gdy define s są automatycznie zmieniane w kodzie przez pre-procesor do wartości rzeczywistej wartości, co oznacza, że ​​jeśli chcesz zmienić wartość dla celów testowych/debugowania, trzeba ponownie skompilować.

+2

Znalazłem, że wiele kompilatorów wypali 'const static ints' na stałe stałe opcode, podobnie jak #defines, co również uniemożliwi zmianę w debugerze. – Crashworks

+0

@Crashworks - czy to zależy od poziomu optymalizacji? – LeopardSkinPillBoxHat

+0

nawet przy wyłączonej optymalizacji kompilatora? –

1

Jeśli jest to coś, co nie jest określone programowo, używam #define. Na przykład, jeśli chcę, aby wszystkie moje obiekty UI miały taką samą przestrzeń między nimi, mógłbym użyć #define kGUISpace 20.

2

Definicje były częścią języka dłuższego niż stałe, więc wiele starszych kodów będzie z nich korzystać, ponieważ określa, gdzie jest jedyny sposób, aby wykonać zadanie po napisaniu kodu. Dla nowszego kodu może to być po prostu kwestia nawyku programisty.

Stałe mają zarówno typ, jak i wartość, więc będą one preferowane wtedy, gdy ma to sens dla wartości, ale nie wtedy, gdy jest beznadziejna (lub polimorficzna).

7

Być może użyłem ich źle, ale przynajmniej w gcc, nie można używać stałych w instrukcjach case.

const int A=12; 
switch (argc) { 
    case A: 
    break; 
} 
3

określić można wykorzystać do wielu celów (bardzo luźne) i należy unikać, jeśli można zastąpić to z const, które zdefiniować zmienną i można zrobić dużo więcej z nim.

W takich przypadkach poniżej, określenie ma być używane

  • przełącznik dyrektywa
  • podstawienie do linii źródłowej
  • makra kod

przykład, w którym trzeba użyć zdefiniować ponad const, gdy masz numer wersji powiedz 3, a chcesz, aby wersja 4 zawierała niektóre metody, które nie są dostępne w wersji 3

#define VERSION 4 
... 

#if VERSION==4 
    ................ 
#endif 
5

Wiele osób podaje porady "w stylu C++". Niektórzy twierdzą nawet, że argumenty C++ dotyczą C. Może to być słuszne. (Niezależnie od tego, czy jest to subiektywne odczucie, czy nie). Ludzie, którzy mówią, że const czasami oznaczają, że coś jest innego w obu językach, są również poprawne.

Ale są to w większości pomniejsze punkty i osobiście, myślę, że tak naprawdę istnieje stosunkowo niewielka konsekwencja, aby iść w obie strony.To kwestia stylu i myślę, że różne grupy ludzi dadzą ci różne odpowiedzi.

Pod względem częstości użytkowania, wykorzystania historycznego i najczęściej spotykanego stylu, w języku C bardziej typowo jest zobaczyć #define. Używanie C-isi w kodzie C może wydawać się dziwne dla pewnego wąskiego segmentu koderów C. (Włączając mnie, to tam leżą moje uprzedzenia.)

Ale jestem zaskoczony, nikt nie zaproponował rozwiązania pośredniego, które "wydaje się właściwe" w obu językach: jeśli pasuje do grupy stałych całkowitych, należy użyć a enum.

1

Oprócz doskonałych powodów podanych przez AndreyT do używania DEFINES zamiast stałych w kodzie "C", jest jeszcze jeden pragmatyczny powód używania DEFINES.

DEFINES są łatwe do zdefiniowania i wykorzystania z (.h) plików nagłówkowych, w których każdy doświadczony koder C mógłby oczekiwać zdefiniowania stałych. Definiowanie stałych w plikach nagłówkowych nie jest takie proste - więcej kodu pozwala uniknąć duplikowania definicji itp.

Również argumenty "typesafe" są mootowe Większość kompilatorów wykryje rażące błędy, takie jak przypisywanie ciągu znaków do i int, lub , "zrobi to, co trzeba" na niewielkim niedopasowaniu, takim jak przypisanie liczby całkowitej do wartości zmiennoprzecinkowej.

1

Makra (definiuje) mogą być używane przez preprocesor, a podczas kompilacji nie mogą być stałe.

Można wykonywać sprawdzanie podczas kompilacji, aby upewnić się, że makro znajduje się w ważnym zakresie (i #error lub #fatal, jeśli nie jest). Możesz użyć wartości domyślnych dla makra, jeśli nie zostało ono jeszcze zdefiniowane. Możesz użyć makra w rozmiarze tablicy.

Kompilator może zoptymalizować z makrami lepiej niż to możliwe ze stałych:

const int SIZE_A = 15; 
#define SIZE_B 15 

for (i = 0; i < SIZE_A + 1; ++i); // if not optimized may load A and add 1 on each pass 
for (i = 0; i < SIZE_B + 1; ++i); // compiler will replace "SIZE_B + 1" with 16 

Większość moich prac jest z wbudowanych procesorów, które nie mają niesamowite zoptymalizowane kompilatory. Może gcc traktuje SIZE_A jak makro na pewnym poziomie optymalizacji.

5

Choć kwestia ta jest specyficzna dla C, myślę, że dobrze jest wiedzieć to:

#include<stdio.h> 
int main() { 
    const int CON = 123; 
    int* A = &CON; 
    (*A)++; 
    printf("%d\n", CON); // 124 in C 
} 

prace w C, ale nie w C++

Jednym z powodów do korzystania #define jest unikanie takich rzeczy, które zepsuć kod, szczególnie jest to mieszanka C i C++.

+0

+1 Dobra wskazówka, nie wiedziałem o tym. – helpermethod

+1

Nie używa wskaźników do zmiany tylko do odczytu w C niezdefiniowane zachowanie? –