Dlaczego ludzie używają zmiennej, aby określić numer PINu, gdy jest mało prawdopodobne, że PIN zmieni się podczas wykonywania kodu?
Wiele razy widzę, int
że używa się definicji pinów,
int led = 13;
kiedy użycie a const int
const int led = 13;
#define LED 13
ma znacznie większy sens.
Jest nawet w tutorialach na stronie Arduino, na przykład w pierwszym tutorialu, który uruchamia większość ludzi, Blink .
Ja czytałem gdzieś , że const int
jest korzystniejszy #define
. Dlaczego nie zachęca się tego od samego początku, zamiast pozwolić ludziom od samego początku rozwijać złe nawyki? Zauważyłem to jakiś czas temu, ale ostatnio zaczęło mnie to irytować, stąd pytanie.
Pamięć / przetwarzanie / computing mądry jest const int
, enum
lub o to chodzi #define
, lepiej niż zwykły int
, czyli zajmuje mniej pamięci, przechowywane w innej pamięci (Flash, EEPROM, SRAM), szybsze wykonanie, szybciej skompilować?
Może się to wydawać duplikatem Czy lepiej używać #define lub const int dla stałych? , ale odpowiadam na pytanie, dlaczego ludzie używają zmiennych, i jak poprawia się wydajność, gdy tego nie robią, zamiast tego, który typ stałej jest lepszy.
źródło
Odpowiedzi:
To jest poprawna metoda. Lub nawet:
Ile masz szpilek?
Niektóre samouczki nie poddały się tak dużej kontroli jakości, jak mogą.
Wydajność będzie lepsza w użyciu
const byte
, w porównaniu zint
tym, że kompilator może być wystarczająco inteligentny, aby zrozumieć, co robisz.To, co możesz zrobić, to delikatnie zachęcić ludzi do korzystania z bardziej wydajnych technik, używając ich we własnym kodzie.
Odpowiedzi na komentarze
byte
Komentator zasugerował, że nie jest to standard C. Jest to poprawne, jednak jest to strona Arduino StackExchange i uważam, że użycie standardowych typów dostarczanych przez Arduino IDE jest dopuszczalne.W Arduino.h jest ta linia:
Pamiętaj, że nie jest to dokładnie to samo co
unsigned char
. Zobacz uint8_t vs unsigned char i Kiedy uint8_t ≠ unsigned char? .Inny komentator zasugerował, że użycie bajtu niekoniecznie poprawi wydajność, ponieważ liczby mniejsze niż
int
będą promowaneint
(patrz Zasady promocji liczb całkowitych, jeśli chcesz więcej na ten temat).Jednak w kontekście identyfikatora stałej kompilator generuje wydajny kod w każdym przypadku. Na przykład deasemblacja „mrugnięcia” daje to w oryginalnej formie:
W rzeczywistości generuje ten sam kod, czy
13
:#define
const int
const byte
Kompilator wie, kiedy może zmieścić liczbę w jednym rejestrze, a kiedy nie. Jednak dobrą praktyką jest stosowanie kodowania, które wskazuje na twoje zamiary . Wyjaśnienie tego
const
jasno pokazuje, że liczba się nie zmieni, a wyjaśnieniebyte
(lubuint8_t
) wyjaśnia, że oczekujesz małej liczby.Mylące komunikaty o błędach
Innym ważnym powodem, dla którego należy unikać,
#define
są komunikaty o błędach wyświetlane w przypadku pomyłki. Rozważmy ten szkic „mrugnięcia”, który zawiera błąd:Na powierzchni wygląda OK, ale generuje te komunikaty o błędach:
Patrzysz na pierwszą podświetloną linię (linia 4) i nawet nie widzisz symbolu „=”. Dodatkowo linia wygląda dobrze. Teraz dość oczywiste jest, na czym polega problem (
= 13
jest zastępowanyLED
), ale gdy linia znajduje się 400 linii dalej w kodzie, nie jest oczywiste, że problem dotyczy sposobu definiowania diody LED.Widziałem ludzi zakochanych wiele razy (w tym siebie).
źródło
int
jest przesada ... to znaczy, aż Arduino wreszcie wyjdzie z planszą Tera ... :-)byte
typu . Masz na myśliunsigned char
.byte
niżint
, ponieważ w większości kontekstów wartość całkowita z typami mniejszymi niżint
są promowaneint
.C doesn't have a byte type. You mean unsigned char.
- Moja odpowiedź była w kontekście Arduino, który ma totypedef uint8_t byte;
. Tak więc w przypadku Arduino używaniebyte
jest OK.Performance won't necessarily be better with byte instead of int
- patrz poprawiony post.Jak słusznie stwierdza Ignacio, dzieje się tak głównie dlatego, że nie wiedzą lepiej. I nie wiedzą lepiej, ponieważ ludzie, którzy ich nauczali (lub zasoby, których używali podczas nauki) nie wiedzieli lepiej.
Znaczna część kodu i samouczków Arduino jest pisana przez ludzi, którzy nigdy nie mieli żadnego szkolenia w programowaniu i są bardzo „samoukami” z zasobów przez ludzi, którzy sami są bardzo samoukami bez odpowiedniego szkolenia w programowaniu.
Wiele fragmentów kodu samouczka, które widzę w tym miejscu (a zwłaszcza te, które są dostępne tylko w filmach na YouTube --- urgh) byłby znakiem nieudanym, gdybym zaznaczał je na egzaminie.
Tak, a
const
jest preferowane zamiast non-const, a nawet ponad a#define
, ponieważ:const
(jak a#define
, w przeciwieństwie do non-const) nie przydziela pamięci RAMconst
(jak non-const, ale w przeciwieństwie do a#define
) nadaje wartości jawny typDrugi punkt jest szczególnie interesujący. O ile nie powiedziano inaczej z osadzonym rzutowaniem typu (
(long)3
) lub sufiksem typu (3L
) lub obecnością kropki dziesiętnej (3.0
),#define
a liczby zawsze będzie liczbą całkowitą, a cała matematyka wykonana na tej wartości będzie tak, jakby była liczba całkowita. Przez większość czasu nie stanowi to problemu, ale możesz natknąć się na ciekawe scenariusze, gdy próbujesz#define
uzyskać wartość większą niż liczba całkowita, którą można zapisać, na przykład#define COUNT 70000
wykonać następnie operację matematyczną z innymiint
wartościami. Używając a,const
możesz powiedzieć kompilatorowi: „Tę wartość należy traktować jako ten typ zmiennej” - więc zamiast tego użyjesz:const long count = 70000;
i wszystko będzie działać zgodnie z oczekiwaniami.Ma również efekt domina, który sprawdza typ podczas przekazywania wartości wokół miejsca. Spróbuj przekazać a
const long
do funkcji, która spodziewa się,int
i narzekałaby na zawężenie zakresu zmiennych (lub nawet całkowicie nie skompilowała się w zależności od scenariusza). Zrób to za pomocą#define
a, a po prostu w ciszy nadal będzie dawał złe wyniki i pozostawiasz drapanie się przez wiele godzin.źródło
const
zmienna może wymagać pamięci RAM, w zależności od kontekstu, np. Jeśli jest inicjowana przy użyciu wartości zwracanej z funkcji innej niż constexpr.const int foo = 13; bar(&foo);
pewno będzie wymagać od kompilatora alokacji faktycznej pamięcifoo
.int
do kompilatora, traktuje tę wartość jako najmniejszy typ, w którym się zmieści (modulo rządzi o podpisanym kontra niepodpisanym). Jeśli korzystasz z systemu, w którymint
jest 16 bitów,#define count 70000
spowoduje to , że będzieszcount
wyglądał jak along
, tak jakby był zdefiniowany jakoconst long count = 70000;
. Ponadto, jeśli przekażesz jedną z tych wersjicount
do oczekiwanej funkcjiint
, każdy rozsądny kompilator potraktuje je tak samo.#define COUNT 70000
taka nie obcina się w liczbę całkowitą , ale kompilator traktuje ją jako typ wystarczająco duży, aby pomieścić tę liczbę. Prawdą jest, że może nie być oczywiste, kiedy używasz,COUNT
że nie jest to int, ale i tak możesz powiedzieć to samoconst long
.COUNT
w swoim przykładzie otrzymuje przed kompilacją z wypowiedzi70000
, która ma typ zdefiniowany przez przepisy literałów, podobnie jak2
lub13L
czy4.0
są określone przez przepisy literałów. Fakt, że używasz#define
do aliasu tych wyrażeń, jest nieistotny. Jeśli chcesz, możesz użyć#define
do aliasu dowolnych fragmentów kodu C.Jako 2-tygodniowy nowicjusz w Arduino podjąłem ogólną ideę zajmowania Arduino przez nie-programistów. Większość szkiców, które zbadałem, w tym te na stronie Arduino, wykazuje całkowity brak porządku, przy czym szkice nie działają i ledwo widzę spójny komentarz. Schematy blokowe nie istnieją, a „Biblioteki” to niemoderowana zbieranina.
źródło
Moja odpowiedź brzmi ... robią to, ponieważ to działa. Trudno mi nie zadać pytania w odpowiedzi, na przykład „dlaczego to musi być„ złe ”?”
źródło