삼항 연산자가 매크로에서 1과 0을 정의하는 데 사용되는 이유는 무엇입니까?


79

임베디드 프로젝트에 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을 얻습니다. 그래서 그들은 같은 것 같습니다. 그들이했던 것처럼 코드를 작성해야하는 이유가 있습니까?


8
아니요, 이유가 없습니다. 당신이 옳습니다.
Art

29
부분적으로 주제에서 벗어남 : 전처리기를 사용하는 광기가 언제 멈추나요? 여기에 관련된 기능에 대한 잠재적 인 다중 평가가 있습니다. 불필요합니다.
스테판

3
때로는 명시적인 것도 좋습니다. 여기서 삼항 연산자는 매크로의 목적이 부울을 반환하는 것임을 한눈에 분명히 보여줍니다.
파이프 라인

5
적어도 누군가가 . 와 같은 것을 시도해도 손상되지 않도록 매크로는 (alpha_char)대신을 사용해야 합니다 . alpha_charATCI_IS_LOWER(true || -1)
저스틴 시간 - 분석 재개 모니카

5
오래전에 쓴 CI처럼 보입니다. 나는 전용했다 파스칼에서 C로 올 것 boolean그래서 같은 공포를 변경 막대한 시간을 낭비, 유형 if (n)if (0 != n)아마 "확인하기"모호한 캐스트를 추가. 나는 if (a < b) ...또한 같은 불평등을 방탄 했다. 물론 그것은 보았다 파스칼의 같은 if a < b then ...,하지만 난 것을 알고 C의가 < 아닌이었다 boolean그러나 int, 그리고이 int될 수 거의 모든 것을 ! 두려움은 금도금으로 이어지고 금도금은 편집증으로 이어지고 편집증은 ... 그런 코드로 이어집니다.
Kevin J. Chase

답변:


131

맞습니다. C에서는 호변입니다. 특정 삼항 조건부 (1 > 0) 유형 int입니다.

하지만 것이다 당신의 삼원 조건식 타입이기 때문에 약간의 호기심이 코너의 경우 (예 : 오버로드 된 함수에 매개 변수로),하지만 C ++에서 문제가 int, 반면 (1 > 0)유형입니다 bool.

제 생각에 저자는 C ++ 호환성을 유지하기 위해 이것에 대해 약간의 생각을했습니다.


2
bool <-> int표준 (통합 변환)의 §4.7 / 4에 의해 C ++에서 변환이 암시 적이 라고 생각했는데 어떻게 중요할까요?
Motun

70
함수의 두 가지 오버로드를 고려하십시오 foo. 하나는 a를 const bool&취하는 다른 하나는 a를 취하는 것 const int&입니다. 그들 중 하나는 당신에게 지불하고 다른 하나는 당신의 하드 디스크를 재구성합니다. 이 경우 올바른 오버로드를 호출하고 있는지 확인할 수 있습니다.
밧세바

3
int삼항을 사용하는 것보다 결과를 캐스팅하여이 경우를 처리하는 것이 더 분명하지 않을까요?
martinkunev

18
@Bathsheba 합법적 인 경우이지만 통합 과부하를 사용하여 이러한 일관되지 않은 동작을 구현하는 프로그래머는 완전히 악합니다.
JAB

7
@JAB : 당신은 그냥 실수로 두 개의 서로 다른 일을하는 것이 코드의 조각을 작성 (또는 더 나쁜 호출하십시오의 (공통) 실수 할 필요가 악 될 필요가 없습니다 정의되지 않은 동작 ) 통합 유형에 따라, 그리고이 근본적으로 다른 코드 경로를 유발할 수있는 장소에서 그렇게하는 것은 불행한 일입니다.

28

비교 결과가 부울이고 산술에서 직접 사용할 수 없다고 생각하는 Linting 도구가 있습니다.

이름에 이름을 붙이거나 손가락을 가리는 것이 아니라 PC-lint는 그러한 보푸라기 도구 입니다.

나는 그들이 옳다고 말하는 것은 아니지만 코드가 왜 그렇게 작성되었는지에 대한 가능한 설명입니다.


10
Not to name names or point any fingers,하지만 둘 다 했어요, 롤.
StackOverflowed

19

숫자 1 또는 0으로 평가 되는 C 표준이 있기 전부터 아주 오래된 코드 에서 이것을 볼 수 있습니다 (x > y). 일부 CPU는 대신 -1 또는 0으로 평가하고 일부 아주 오래된 컴파일러가 따라 갔을 수 있으므로 일부 프로그래머는 추가 방어력이 필요하다고 느꼈습니다.

비슷한 식이 반드시 숫자 1 또는 0으로 평가되는 것은 아니기 때문에 때때로 이것을 보게 될 것입니다. 예를 들어, in

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

내측 &0 또는 숫자의 값으로 평가하여 -expression F_DO_GRENFELZ아마, 아니 그래서 1, ? 1 : 0이를 정규화하는 역할을한다. 나는 개인적으로 그것을 다음과 같이 쓰는 것이 더 명확하다고 생각합니다.

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

그러나 합리적인 사람들은 동의하지 않을 수 있습니다. 여러 종류의 표현을 테스트하면서 여러 종류의 표현을 연속해서 가지고 있다면, 실제로 어떤 표현이 필요한지 걱정하는 것보다 모든 표현? 1 : 0끝에 추가하는 것이 더 유지 관리하기 쉽다고 누군가 결정했을 것입니다.


전반적 !!( expr )으로 부울을 정규화 하는 데 사용하는 것을 선호 하지만 익숙하지 않은 경우 혼란 스럽습니다.
PJTraill

1
@PJTraill 괄호 안에 공백을 넣을 때마다 하나님은 새끼 고양이를 죽 이십니다. 부디. 새끼 고양이를 생각하십시오.
zwol

이것이 C 프로그램에서 괄호 안에 공백을 넣지 말라고 들었던 가장 좋은 이유입니다.
PJTraill

15

SDK 코드에 버그가 있으며 삼항은 아마도 그것을 고치기위한 곤경이었을 것입니다.

매크로이기 때문에 인수 (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 '{'**

4
: 다른 버그 부작용 인수는 예상치 못한 결과를 야기 할 수 있도록 파라미터를 두 번 사용된다는 것이다 IS_LOWER(++ var)증분시킬 수도 var별도로 예고하지 않을 수 있으며 소문자 인식 한두번 'z'경우 var였다 'y'매크로 호출 전에. 그렇기 때문에 이러한 매크로를 피하거나 인수를 함수에 전달해야합니다.
CiaPan

5

C에서는 중요하지 않습니다. C의 부울 표현식에는 또는 인 int값과 유형이 있으므로01

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

}

의도 된 효과가 없을 수도 있으며, 작성자는 코드를 더 명확하게 만들었다 고 생각했습니다.


BTW, 나는 C가 스위트를 따라 부울 표현식을 만들어야한다고 생각합니다 . _Bool이제 C가 _Bool_Generic. int어쨌든 대부분의 컨텍스트에서 모든 작은 유형이 자동으로 승격된다는 점을 감안할 때 많은 코드를 깨서는 안됩니다 .
PSkocik

5

한 가지 간단한 설명은 어떤 사람들은 조건이 C에서 동일한 값을 반환한다는 것을 이해하지 못하거나 작성하는 것이 더 깨끗하다고 ​​생각한다는 것입니다 ((a>b)?1:0).

이는 일부 사람들이 적절한 부울을 가진 언어에서 유사한 구문을 사용하는 이유를 설명합니다 .C 구문에서는 (a>b)?true:false).

이것은 또한이 매크로를 불필요하게 변경해서는 안되는 이유를 설명합니다.


0

아마도 임베디드 소프트웨어라면 단서를 얻을 수있을 것입니다. ACTI 라인이 반전 논리가 아닌 직접 논리를 사용한다는 것을 쉽게 암시하기 위해이 스타일을 사용하여 작성된 많은 매크로가있을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.