Używam zestawu SDK do osadzonego projektu. W tym kodzie źródłowym znalazłem kod, który przynajmniej wydał mi się dziwny. W wielu miejscach w SDK jest kod źródłowy w tym formacie:
#define ATCI_IS_LOWER( alpha_char ) ( ( (alpha_char >= ATCI_char_a) && (alpha_char <= ATCI_char_z) ) ? 1 : 0 )
#define ATCI_IS_UPPER( alpha_char ) ( ( (alpha_char >= ATCI_CHAR_A) && (alpha_char <= ATCI_CHAR_Z) ) ? 1 : 0 )
Czy użycie tutaj operatora trójskładnikowego ma znaczenie?
Nie jest
#define FOO (1 > 0)
taki sam jak
#define BAR ( (1 > 0) ? 1 : 0)
?
Próbowałem to ocenić, używając
printf("%d", FOO == BAR);
i otrzymujemy wynik 1, więc wydaje się, że są równe. Czy istnieje powód, aby pisać kod tak, jak oni?
c++
c
boolean
ternary-operator
Viktor S
źródło
źródło
(alpha_char)
zamiast tegoalpha_char
, aby upewnić się, że nie zepsują się, gdy ktoś spróbuje czegoś szalonegoATCI_IS_LOWER(true || -1)
.boolean
typ, więc zmarnowany czas zmienia niewypowiedziane okropności jakif (n)
sięif (0 != n)
zapewne dodanie wątpliwy obsady „aby upewnić się”. Jestem pewien, że ja też zabezpieczałem nierównościif (a < b) ...
. Pewnie, że wyglądał jak Pascalaif a < b then ...
, ale wiedziałem, że C na<
nie byłoboolean
aleint
, iint
może być prawie wszystko ! Strach prowadzi do złocenia, złocenie prowadzi do paranoi, paranoja prowadzi do ... takiego kodu.Odpowiedzi:
Masz rację, w C jest tautologiczna. Zarówno twoje szczególne trójskładnikowe warunki warunkowe, jak i
(1 > 0)
są typuint
.Ale miałoby to znaczenie w C ++, w niektórych osobliwych przypadkach narożnych (np. Jako parametry przeciążonych funkcji), ponieważ twoje trójskładnikowe wyrażenie warunkowe jest typu
int
, a(1 > 0)
jest typubool
.Domyślam się, że autor przemyślał to, mając na uwadze zachowanie zgodności z C ++.
źródło
bool <-> int
konwersje są niejawne w C ++ przez §4.7 / 4 ze standardu (konwersja całkowa), więc jakie to ma znaczenie?foo
, z których jeden przyjmuje aconst bool&
drugiconst int&
. Jeden z nich płaci, a drugi formatuje dysk twardy. W takim przypadku warto się upewnić, że wywołujesz prawidłowe przeciążenie.int
zamiast używania trójskładnika?Istnieją narzędzia do lintingu, które uważają, że wynik porównania jest logiczny i nie można ich używać bezpośrednio w arytmetyce.
Nie nazywanie nazwisk ani wskazywanie palcami, ale PC-lint jest takim narzędziem do robienia kłaczków .
Nie mówię, że mają rację, ale jest to możliwe wyjaśnienie, dlaczego kod został napisany w ten sposób.
źródło
Not to name names or point any fingers,
ale zrobiłeś jedno i drugie, lol.Czasami zobaczysz to w bardzo starym kodzie, sprzed istnienia standardu C do przeliterowania, którego wynikiem
(x > y)
jest numeryczna 1 lub 0; niektóre procesory wolałyby zamiast tego oszacować to na -1 lub 0, a niektóre bardzo stare kompilatory mogły po prostu to zrobić, więc niektórzy programiści uznali, że potrzebują dodatkowej ochrony.Będziesz czasami zobaczyć także to, ponieważ podobne wyrażenia nie koniecznie ocenić na numerycznej 1 lub 0. Na przykład w
#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)
&
wyrażenie wewnętrzne ma wartość 0 lub wartość liczbowąF_DO_GRENFELZ
, która prawdopodobnie nie jest 1, więc? 1 : 0
służy do kanonizacji. Osobiście uważam, że jaśniej jest napisać to jako#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)
ale rozsądni ludzie mogą się nie zgodzić. Jeśli miał całą masę nich w rzędzie, testując różne rodzaje wyrażeń, ktoś zdecydowaliśmy, że to było bardziej linkujących umieścić
? 1 : 0
na końcu wszyscy z nich, niż martwić się, które z nich rzeczywiście potrzebne.źródło
!!( expr )
do kanonizacji wartości logicznej, ale przyznam, że jest to mylące, jeśli nie jesteś z nią zaznajomiony.W kodzie SDK jest błąd, a trójnik prawdopodobnie nie byłby w stanie go naprawić.
Będąc makrem, argumenty (alpha_char) mogą być dowolnym wyrażeniem i powinny być umieszczone w nawiasach, ponieważ wyrażenia takie jak „A” i „c” nie przejdą testu.
#define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ? 1 : 0 ) std::cout << IS_LOWER('A' && 'c'); **1** std::cout << IS_LOWER('c' && 'A'); **0**
Dlatego w rozwinięciu zawsze należy umieszczać argumenty makr w nawiasach.
W twoim przykładzie (ale z parametrami) oba są błędne.
#define FOO(x) (x > 0) #define BAR(x) ((x > 0) ? 1 : 0)
Najbardziej poprawnie zostałyby zastąpione przez
#define BIM(x) ((x) > 0)
@CiaPan To świetny punkt w następującym komentarzu, który mówi, że użycie parametru więcej niż jeden raz prowadzi do nieokreślonych rezultatów. Na przykład
#define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z')) char ch = 'y'; std::cout << IS_LOWER(ch++); **1** **BUT ch is now '{'**
źródło
IS_LOWER(++ var)
może zwiększać sięvar
raz lub dwa razy, dodatkowo może nie zauważyć i rozpoznać małe litery,'z'
jeślivar
było'y'
przed wywołaniem makra. Dlatego należy unikać takich makr lub po prostu przekazywać argument do funkcji.W C to nie ma znaczenia. Wyrażenia logiczne w C mają typ
int
i wartość albo0
albo1
, więcConditionalExpr ? 1 : 0
nie ma żadnego efektu.
W C ++ jest to efektywne rzutowanie na
int
, ponieważ wyrażenia warunkowe w C ++ mają typbool
.#include <stdio.h> #include <stdbool.h> #ifndef __cplusplus #define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") ); #else template<class T> int print_type(T const& x); template<> int print_type<>(int const& x) { return puts("int"); } template<> int print_type<>(bool const& x) { return puts("bool"); } #endif int main() { print_type(1); print_type(1 > 0); print_type(1 > 0 ? 1 : 0); /*c++ output: int int int cc output: int bool int */ }
Możliwe jest również, że żaden efekt nie był zamierzony, a autor po prostu pomyślał, że dzięki temu kod jest jaśniejszy.
źródło
_Bool
, teraz, gdy C ma_Bool
i_Generic
. Nie powinno to zepsuć zbyt wiele kodu, biorąc pod uwagę, że wszystkie mniejsze typy i tak są automatycznie przenoszone doint
większości kontekstów.Jednym prostym wyjaśnieniem jest to, że niektórzy ludzie albo nie rozumieją, że warunek zwróciłby tę samą wartość w C, albo myślą, że pisanie jest czystsze
((a>b)?1:0)
.To wyjaśnia, dlaczego niektórzy używają podobnych konstrukcji również w językach z odpowiednimi parametrami logicznymi, którymi byłyby w C-składni
(a>b)?true:false)
.To również wyjaśnia, dlaczego nie powinieneś niepotrzebnie zmieniać tego makra.
źródło
Być może bycie oprogramowaniem wbudowanym dałoby kilka wskazówek. Być może jest wiele makr napisanych w tym stylu, aby łatwo wskazać, że linie ACTI używają logiki bezpośredniej zamiast logiki odwróconej.
źródło