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?
Odpowiedz
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.
+1 dla zrozumienia i prawidłowego artykułowania różnic C/C++, punktu, który wydaje się być utracony na wielu ludziach. – asveikau
Dobra odpowiedź. Dziękuję za zaznaczenie problemu ze zmiennymi "const" dla deklarowania rozmiarów macierzy. –
+1 Wow, świetna odpowiedź – helpermethod
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ć.
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
@Crashworks - czy to zależy od poziomu optymalizacji? – LeopardSkinPillBoxHat
nawet przy wyłączonej optymalizacji kompilatora? –
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
.
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).
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;
}
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
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
.
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.
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.
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++.
+1 Dobra wskazówka, nie wiedziałem o tym. – helpermethod
Nie używa wskaźników do zmiany tylko do odczytu w C niezdefiniowane zachowanie? –
- 1. CodeIgniter funkcje ścieżka definicje
- 2. Czy powinienem preferować MonadUnliftIO lub MonadMask dla funkcji bracketingu?
- 3. Definicje rekursywne w pandach
- 4. Czy powinienem preferować hashe lub hashrefy w Perlu?
- 5. Definicje typów warunkowych
- 6. Zalety wysyłki znaczników ponad normalną rozdzielczość przeciążenia
- 7. Stałe klasy
- 8. Definicje kwantów w R
- 9. Narysuj prostokąt ponad obrazkiem
- 10. Czy konieczne jest stałe pobieranie w RXTX?
- 11. Czy można oceniać stałe w podwójnym cudzysłowie?
- 12. Czy stałe statyczne funkcji inline są unikalne?
- 13. Czy członkowie tablicy constexpr kompilują stałe czasowe?
- 14. Czy globalne stałe są anty-wzorcami?
- 15. C# - czy wszystkie są stałe Enum?
- 16. Iteracja ponad * argumenty?
- 17. Kiedy powinieneś preferować ReBuild zamiast Build?
- 18. Dlaczego preferować właściwości dla zmiennych publicznych?
- 19. Entitymanager.flush() VS EntityManager.getTransaction(). Commit - co powinienem preferować?
- 20. Czy można zastąpić definicje kroków w kontekście zachowania?
- 21. Reguły i definicje podświetlania składni
- 22. kruszywa funkcje ponad tablic
- 23. Czy jest to niejednoznaczna gramatyka? Jak mam to rozwiązać?
- 24. Interpreter Haskella/w definicje typu
- 25. Definicje gramatyki EBNF dla PHP?
- 26. dane ponad kanału audio
- 27. EAV ponad SQL Server
- 28. Definicje szablonów poza treścią klasy
- 29. definicje typu z otwartymi związków
- 30. NUMER WIERSZY() PONAD
Uważaj na zespół sztokholmski! – Potatoswatter
[Ta odpowiedź] (http://stackoverflow.com/a/1674459/2235567) jest trafna. –
możliwy duplikat ["static const" vs "#define" w C] (http://stackoverflow.com/questions/1674032/static-const-vs-define-in-c) –