[c] 부울과 비트 비교

uint16_t로 인코딩 된 플래그 세트가 있다고 가정 해보십시오 flags. 예를 들면 다음과 같습니다 AMAZING_FLAG = 0x02. 이제 기능이 있습니다. 이 기능을 사용하려면 플래그를 변경해야하는지 확인해야합니다. 변경하려면 플래시에 기록해야하기 때문입니다. 그리고 그것은 비싸다. 따라서 나는 flags & AMAZING_FLAG같은지 알려주는 검사를 원합니다.doSet . 이것이 첫 번째 아이디어입니다.

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}

이것은 직관적 인 if 문이 아닙니다. 더 좋은 방법이 있어야한다고 생각합니다.

if ((flags & AMAZING_FLAG) != doSet){

}

그러나 이것은, 실제로 일을하지 않는 true동일하게 보인다 0x01.

그렇다면 부울과 비트를 비교할 수있는 깔끔한 방법이 있습니까?



답변

0이 아닌 숫자를 1 (true)로 변환하려면 오래된 트릭이 있습니다. !(not) 연산자를 두 번 적용하십시오 .

if (!!(flags & AMAZING_FLAG) != doSet){


답변

당신은 C의 값에 해당하는 부울 문에 비트 마스크를 변환해야 0하거나 1.

  • (flags & AMAZING_FLAG) != 0. 가장 일반적인 방법입니다.

  • !!(flags & AMAZING_FLAG). 다소 일반적이지만 사용하기는 쉽지만 약간 암호입니다.

  • (bool)(flags & AMAZING_FLAG). C99 이상의 최신 C 웨이.

다음 부울 사용과 비교, 위의 대안 중 하나를 가지고 !===.


답변

논리적 관점에서 보면 flags & AMAZING_FLAG다른 모든 플래그를 마스킹하는 비트 작업 만 있습니다. 결과는 숫자 값입니다.

부울 값을 받으려면 비교를 사용하십시오.

(flags & AMAZING_FLAG) == AMAZING_FLAG

이제이 논리 값을와 비교할 수 있습니다 doSet.

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)

C에서는 숫자의 부울 값으로의 암시 적 변환 규칙으로 인해 약어가있을 수 있습니다. 그래서 당신은 또한 쓸 수 있습니다

if (!(flags & AMAZING_FLAG) == doSet)

더 간결하게 쓰려고 그러나 이전 버전은 가독성 측면에서 더 좋습니다.


답변

doSet값을 기준으로 마스크를 만들 수 있습니다 .

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

이제 수표는 다음과 같습니다.

setAmazingFlag(bool doSet)
{
    const uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

    if (flags & set_mask) {
        // Really expensive thing
        // Update flags
    }
}

일부 아키텍처에서는 !!분기로 컴파일 될 수 있으며이 경우 두 개의 분기가있을 수 있습니다.

  1. 에 의한 정규화 !!(expr)
  2. 와 비교 doSet

내 제안의 장점은 보장 된 단일 지점입니다.

참고 : 30을 초과하여 왼쪽으로 시프트하여 정의되지 않은 동작을 도입하지 않도록하십시오 (정수는 32 비트라고 가정). 이것은 쉽게 달성 할 수 있습니다static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");


답변

이 테스트를 수행하는 방법에는 여러 가지가 있습니다.

삼항 연산자는 값 비싼 점프를 생성 할 수 있습니다.

if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0))

부울 변환을 사용할 수도 있습니다.

if (!!(flags & AMAZING_FLAG) != doSet)

또는 동등한 대안 :

if (((flags & AMAZING_FLAG) != 0) != doSet)

곱셈이 저렴한 경우 다음을 사용하여 점프를 피할 수 있습니다.

if ((flags & AMAZING_FLAG) != doSet * AMAZING_FLAG)

경우 flags서명하고 컴파일러는 매우 똑똑 아래 부문은 간단한 변화로 컴파일 할 수 있습니다

if ((flags & AMAZING_FLAG) / AMAZING_FLAG != doSet)

아키텍처가 2의 보수 산술을 사용하는 경우 다른 대안이 있습니다.

if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))

또는 flags비트 필드가있는 구조로 정의 하고 훨씬 더 읽기 쉬운 구문을 사용할 수 있습니다.

if (flags.amazing_flag != doSet)

아아, 비트 필드의 사양은 비트 레벨 구현에 대한 정밀한 제어를 허용하지 않기 때문에이 접근법은 일반적으로 눈살을 찌푸립니다.


답변