web-dev-qa-db-ja.com

マクロで3項演算子を使用して1と0を定義するのはなぜですか?

埋め込みプロジェクトにSDKを使用しています。このソースコードで、少なくとも奇妙なコードを見つけました。 SDKの多くの場所には、次の形式のソースコードがあります。

#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 )

ここで三項演算子を使用すると違いが生じますか?

じゃない

#define FOO (1 > 0)

と同じ

#define BAR ( (1 > 0) ? 1 : 0)

を使用して評価してみました

printf("%d", FOO == BAR);

結果1が得られるので、それらは等しいようです。彼らがやったようにコードを書く理由はありますか?

79
Viktor S

比較の結果はブール値であり、算術演算で直接使用することはできないという意見のリンティングツールがあります。

名前を付けたり指を向けたりするのではなく、 PC-lintはそのようなリンティングツールです

私は彼らが正しいと言っているわけではありませんが、それはコードがそのように書かれた理由の可能な説明です。

29
unwind

(x > y)が数値1または0に評価されることを説明するC標準が存在する前から、非常に古いコードでこれを見ることがあります。代わりに-1または0に評価するCPUもあれば、非常に古いコンパイラーがたどり着いたばかりのCPUもあるため、一部のプログラマーは追加の防御が必要だと感じました。

similar expressions do n'tは必ず数値1または0に評価されるため、これも表示されることがあります。たとえば、

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)

内側の&- expressionは0またはF_DO_GRENFELZの数値に評価されますが、これはおそらくnot 1であるため、? 1 : 0は正規化に役立ちます。私はそれを次のように書く方が明確だと思う

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)

しかし、合理的な人々は反対することができます。さまざまな種類の式をテストしてこれらの束を一列に並べた場合、誰かがallの最後に? 1 : 0を置く方が保守しやすいと判断したかもしれませんどの人が実際にそれを必要としていたかを心配します。

20
zwol

SDKコードにはバグがあり、3項はおそらくそれを修正するための手がかりでした。

マクロであるため、引数(alpha_char)は任意の式にすることができ、 'A' && 'c'などの式はテストに失敗するため、括弧で囲む必要があります。

#define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ?  1 : 0 )
std::cout << IS_LOWER('A' && 'c');
**1**
std::cout << IS_LOWER('c' && 'A');
**0**

これが、拡張のマクロ引数を常に括弧で囲む必要がある理由です。

そのため、例では(ただしパラメーターを使用して)これらは両方ともバグがあります。

#define FOO(x) (x > 0)
#define BAR(x) ((x > 0) ? 1 : 0)

それらは最も正確に置き換えられます

#define BIM(x) ((x) > 0)

@CiaPanは、パラメーターを複数回使用すると結果が定義できないというコメントをフォローする際に大きなポイントになります。例えば

#define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z'))
char ch = 'y';
std::cout << IS_LOWER(ch++);
**1** 
**BUT ch is now '{'**
16
Konchog

簡単な説明の1つは、条件がCで同じ値を返すことを理解していないか、((a>b)?1:0)

これは、C構文では(a>b)?true:false)

これは、このマクロを不必要に変更してはならない理由も説明しています。

5
Hans Olsson

Cでは、それは問題ではありません。 Cのブール式の型はintであり、値は0 または 1、 そう

ConditionalExpr ? 1 : 0

効果がありません。

C++では、C++の条件式の型はintであるため、事実上boolへのキャストです。

#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
*/

}

効果が意図されていなかった可能性もあり、著者はコードがより明確になったと単純に考えました。

5
PSkocik

たぶん、組み込みソフトウェアであるため、いくつかの手がかりが得られるでしょう。このスタイルを使用して記述された多くのマクロがあり、ACTI行が反転ロジックではなく直接ロジックを使用するという簡単なヒントがあるかもしれません。

0
J.Guarin