2010-01-21 3 views
5

Я очень рад, что в C, такие вещи, как это плохо код:Как аккуратно избежать C бросает потерять правду

(var_a == var_b) ? TRUE : FALSE 

Однако, что это лучший способ справиться с этим:

/* Header stuff */ 
#define INTERESTING_FLAG 0x80000000 
typedef short int BOOL; 

void func(BOOL); 

/* Code */ 
int main(int argc, char *argv[]) 
{ 
     unsigned long int flags = 0x00000000; 

     ... /* Various bits of flag processing */ 

     func(flags & INTERESTING_FLAG); /* func never receives a non-zero value 
             * as the top bits are cut off when the 
             * argument is cast down to a short 
             * int 
             */ 
} 

Допустимо ли (для любого приемлемого использования) (flags & FLAG_CONST) ? TRUE : FALSE?

+4

Я думаю, что проблема здесь '#define BOOL короткий int'. Вы определили так называемый логический тип, но где ненулевые значения преобразуются в него как false. Затем вы действовали так, как будто любое ненулевое значение истинно. Это какая-то непроницаемая абстракция. –

+0

Я действительно не понимаю, что плохого в этом коде, это стандартная функция языка, о которой должен знать любой программист на C. – Skizz

+1

@Skizz: проблема с '(var1 == var2)? TRUE: FALSE: 'вы имеете в виду? Проблема в том, что она лучше выражается как '(var1 == var2)', если TRUE и FALSE имеют разумные значения. –

ответ

5

Я бы предпочел (flags & CONST_FLAG) != 0. Еще лучше используйте тип _Bool, если он у вас есть (хотя он часто замаскирован под bool).

+0

Does '_Bool' /' bool' отображать все ненулевые значения в 'true', то? Я не слышал об этом раньше ... –

+0

@me_and: да, приведение в _Bool/bool делает все ненулевые значения true и zero false. Он был добавлен в C99, но поскольку C++ также имеет 'bool', в него также входит множество компиляторов C, даже если они не реализуют (даже близко) все C99. –

+0

Принятие: имеет хорошую опцию pre-C99 (что, вероятно, мне придется использовать, я должен уметь строить на компиляторах, отличных от C99, к сожалению), а также имеет идеальное стандартное решение: D –

0

Вы можете избежать этого несколько различных способов:

Во-первых

void func(unsigned long int); 

будет заботиться о нем ...

Или

if(flags & INTERESTING_FLAG) 
{ 
    func(true); 
} 
else 
{ 
    func(false); 
} 

также сделать бы это ,

EDIT: (flags & INTERESTING_FLAG) != 0 также хорош. Наверное, лучше.

4

Установите флаги вашего компилятора как можно более, чтобы предупредить вас о любом актере, который теряет биты и обрабатывает предупреждения как ошибки.

+0

+1. Это не говорит вам, что с этим делать, но это важно. –

+0

Это говорит мне о проблеме, но это не решает ее ... –

6

В любом случае я бы назвал func с (flags & INTERESTING_FLAG) != 0 в качестве аргумента, чтобы указать, что требуется логический параметр, а не арифметический результат flags & INTERESTING_FLAG.

+2

Я смутно частичный ((Флаги и Маска) == МАСК) как общее решение, но он должен дать тот же результат, что и! = 0. – Vatine

+0

@vatine: Это имеет тот недостаток, что сравнение с ненулевыми значениями (очень незначительно) менее эффективно, чем сравнение с нулем, поскольку сравнение с нолем обычно может быть реализовано как одна операция с машиной. Не имеет большого значения (и с хорошим компилятором он вообще ничего не меняет), но все же ... –

+0

@me_and: Никакой разницы вообще с двумя компиляторами, которые я только что пробовал. Это довольно простое преобразование для компилятора. –

0

Это частично от темы:

Я бы также создать функцию справки, которая делает его очевидным для читателя, что цель проверки так вы не заполнили свой код с проверкой все это явный признак над местом. Типизация типа флага облегчит изменение типа флага и реализации позже.

Современные компиляторы поддерживают встроенное ключевое слово, которое может избавиться от служебных накладных расходов при вызове функции.

typedef unsigned long int flagtype; 
... 
inline bool hasInterestingFlag(flagtype flags) { 
    return ((flags & INTERESTING_FLAG) != 0); 
} 
1

Это не может быть популярным решением, но иногда макросы полезны.

#define to_bool(x) (!!(x)) 

Теперь мы можем спокойно иметь все, что угодно, не опасаясь отказа нашего типа:

func(to_bool(flags & INTERESTING_FLAG)); 

Другой альтернативой может быть определить свой логический тип, чтобы быть intmax_t (от stdint.h), так что это невозможно для значения, которое будет усечено в ложность.

В то время как я здесь, я хочу сказать, что вы должны использовать typedef для определения нового типа, а не #define:

typedef short Bool; // or whatever type you end up choosing 

Некоторые могут возразить, что вы должны использовать const переменную вместо макрос для числовых констант:

const INTERESTING_FLAG = 0x80000000; 

В целом есть лучшие вещи, на которые вы можете потратить свое время. Но макросы для typedef s немного глупы.

+0

Хорошее место. Я использовал typedef в своем коде и полностью забыл о его существовании при написании этого вопроса! –

4

Некоторым людям это не нравится, но я использую !!.

т.е.

!!(flags & CONST_FLAG) 

(не как to_bool макрос, как кто-то другой предложил, просто прямо в коде).

Если больше людей использовали его, это не было бы необычным, так что начните использовать его!

+0

Я не думаю, что это странно или необычно, мне это не нравится. Я предпочитаю названные операторы, где это возможно ('и' вместо' && ',' not' вместо '!'), И если бы я увидел, что кто-то напишет 'not not x' в Python, я бы застрелил их. Версия символа более читаема, но мне все равно это не нравится, поэтому я поместил ее в макрос. –

0

У вас есть что-нибудь против

flags & INTERESTING_FLAG ? TRUE : FALSE 

?

0

Вот почему вы должны использовать значения только в «логическом» способе, когда эти значения имеют явно логическую семантику. Ваше значение не удовлетворяет правилу taht, поскольку оно имеет выраженную целую семантику (или, точнее, семантику бит-массива). Для того, чтобы преобразовать такое значение булева, сравнить его с 0

func((flags & INTERESTING_FLAG) != 0); 
Смежные вопросы