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
}
}
일부 아키텍처에서는 !!
분기로 컴파일 될 수 있으며이 경우 두 개의 분기가있을 수 있습니다.
- 에 의한 정규화
!!(expr)
- 와 비교
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)
아아, 비트 필드의 사양은 비트 레벨 구현에 대한 정밀한 제어를 허용하지 않기 때문에이 접근법은 일반적으로 눈살을 찌푸립니다.