C ++에서 부울에 비트 연산자 사용


84

C ++에서 "bool"값에 대해 비트 연산자 &, |, ^를 사용하지 않는 이유가 있습니까?

때때로 두 조건 중 정확히 하나가 참 (XOR)이되기를 원하는 상황이 발생하므로 ^ 연산자를 조건식에 넣습니다. 또한 때때로 결과가 참인지 아닌지 (단락이 아닌) 조건의 모든 부분을 평가하기를 원하므로 & 및 |를 사용합니다. 또한 때때로 부울 값을 누적해야하며 & = 및 | =는 매우 유용 할 수 있습니다.

이 작업을 할 때 몇 가지 눈썹을 올렸지 만 코드는 여전히 의미 있고 명확합니다. bool에 이것을 사용하지 않을 이유가 있습니까? 이에 대해 나쁜 결과를 제공하는 최신 컴파일러가 있습니까?


이 질문을 한 후 C ++에 대해 충분히 배웠다면 다시 돌아와서 현재 답변을 받아들이지 말고 단락 차이를 올바르게 설명하는 Patrick Johnmeyer의 대답을 받아 들여야합니다.
codetaku

사람들이 이것이 좋은 생각인지 의문을 제기하기 때문에 일반적인 발언. 단위 테스트의 분기 범위에 관심이 있다면 분기를 줄이는 것이 바람직합니다. 이를 위해 비트 연산자가 유용합니다.
qznc

답변:


61

||&&부울 연산자가 있으며이 내장 된 것들 복귀 하나에 보장 true또는 false. 다른 건 없습니다.

|, &^비트 연산자입니다. 작업하는 숫자의 영역이 1과 0이면 정확히 동일하지만 C 언어의 경우처럼 부울이 엄격하게 1과 0이 아닌 경우 일부 동작이 발생할 수 있습니다. 당신은 원하지 않았습니다. 예를 들면 :

BOOL two = 2;
BOOL one = 1;
BOOL and = two & one;   //and = 0
BOOL cand = two && one; //cand = 1

그러나 C ++에서는 bool유형이 a true또는 a false(암시 적으로 각각 1및 로 변환) 로만 보장 0되므로이 입장에서는 걱정할 필요가 없지만 사람들이 코드에서 이러한 것을 보는 데 익숙하지 않다는 사실 하지 않는 것에 대해 좋은 주장을합니다. 그냥 말하고 b = b && x끝내십시오.


C ++에서 논리 xor 연산자 ^^ 같은 것은 없습니다. 두 인수에 대해 bools! =는 동일한 작업을 수행하지만 두 인수 중 하나가 bool이 아니면 위험 할 수 있습니다 (논리적 xor에 ^를 사용하는 것처럼). 이것이 어떻게 받아 들여 졌는지 확실하지 않습니다 ...
Greg Rogers

1
돌이켜 보면 그건 멍청한 일이었습니다. 어쨌든 문제를 해결하기 위해 예제를 &&로 변경했습니다. 예전에 언젠가 ^^를 사용했다고 맹세하지만, 그렇지 않았을 것입니다.
Patrick

C는 현재 10 년 이후로 0 또는 1 만 될 수있는 bool 유형을 가지고 있습니다. _Bool
Johannes Schaub-litb

16
그렇지 않은 짧은 circut 않기 때문에 그들은 심지어 bools에 대해 여전히 같은 없습니다
aaronman

4
예, "당신이 운영하는 숫자의 영역이 단지 1과 0 일 때, 그것들은 정확히 동일 할 때"를 "당신이 운영하는 숫자의 영역이 단지 1 일 때"로 변경하기 전까지는 이것이 받아 들여지는 대답이되어서는 안된다고 강력하게 믿습니다. 0, 유일한 차이점은 비트 연산자가 단락되지 않는다는 것입니다. " 앞의 진술은 완전히 틀 렸기 때문에 그 동안 나는 그 문장이 포함되어 있기 때문에이 답변을 반대표를 던졌습니다.
codetaku

32

두 가지 주요 이유. 요컨대, 신중하게 고려하십시오. 그것에 대한 좋은 이유가있을 수 있지만, 당신의 코멘트에 매우 명백한 이유가 있다면 그것은 깨지기 쉬울 수 있고 당신이 말했듯이 사람들은 일반적으로 이와 같은 코드를 보는 데 익숙하지 않기 때문입니다.

비트 xor! = 논리 xor (0 및 1 제외)

첫째, falsetrue(또는 01, 정수) 이외의 값 에 대해 ^연산 하는 경우 연산자는 논리 xor와 동일하지 않은 동작을 도입 할 수 있습니다. 예를 들면 :

int one = 1;
int two = 2;

// bitwise xor
if (one ^ two)
{
  // executes because expression = 3 and any non-zero integer evaluates to true
}

// logical xor; more correctly would be coded as
//   if (bool(one) != bool(two))
// but spelled out to be explicit in the context of the problem
if ((one && !two) || (!one && two))
{
  // does not execute b/c expression = ((true && false) || (false && true))
  // which evaluates to false
}

이것을 먼저 표현한 사용자 @Patrick에게 감사드립니다.

작업 순서

둘째, |, &,과 ^, 비트 연산자로, 단락을하지 않습니다. 또한 3 개의 연산이 모두 일반적으로 교환 적이기 때문에 단일 명령문에 함께 연결된 여러 비트 연산자 (명시 적 괄호 포함)는 컴파일러를 최적화하여 재정렬 할 수 있습니다. 이것은 작업 순서가 중요한 경우에 중요합니다.

다시 말해

bool result = true;
result = result && a() && b();
// will not call a() if result false, will not call b() if result or a() false

항상 동일한 결과 (또는 최종 상태)를 제공하지는 않습니다.

bool result = true;
result &= (a() & b());
// a() and b() both will be called, but not necessarily in that order in an
// optimizing compiler

당신이 방법을 제어 할 수 있기 때문에 특히 중요 a()하고 b(), 또는 다른 사람이 따라 올 수 및 종속성을 이해하지 나중에 변경하고 불쾌한 (자주 릴리스 빌드 전용) 버그가 발생합니다.


한 경우에는 bool로 캐스트하고 다른 경우에는 캐스트하지 않으면 실제로 공정한 비교가 아닙니다. 두 경우 모두 bool로 캐스트하면 제대로 작동하고 왜 깨지기 쉬운 지 (기억해야하기 때문에).
Greg Rogers

단락에 대한 설명은 이것이 절대적으로 허용되는 대답이어야하는 이유입니다. 패트릭의 대답 (오류 ... 패트릭이라는 이름의 다른 패트릭)은 "당신이 조작하는 숫자의 영역이 단지 1과 0 일 때, 그것들은 정확히 똑같습니다"라고 말하는 데 완전히 틀 렸습니다.
codetaku

13

나는 생각한다

a != b

당신이 원하는 것입니다


1
이것은 사실입니다, +1. 그러나이 연산자의 할당 버전이 없습니다 (!== 즉) bool값 시퀀스의 XOR을 계산하는 경우 acc = acc!= condition(i);루프 본문 에 작성해야합니다 . 컴파일러는 아마도 이것을 !==존재하는 것처럼 효율적으로 처리 할 수 있지만 일부는보기에 좋지 않다는 것을 발견하고 부울을 정수로 추가 한 다음 합계가 홀수인지 테스트하는 대안을 선호 할 수 있습니다.
마크 반 리웬

10

올려 진 눈썹은 그것을 멈출만큼 충분히 말해 줄 것입니다. 컴파일러 용 코드를 작성하는 것이 아니라 동료 프로그래머 용으로 작성한 다음 컴파일러 용으로 작성합니다. 컴파일러가 작동하더라도 다른 사람들을 놀라게하는 것은 원하는 것이 아닙니다. 비트 연산자는 bool이 아닌 비트 연산을위한 것입니다.
포크로 사과도 먹어? 작동하지만 사람들을 놀라게하므로하지 않는 것이 좋습니다.


3
예, 이것이 최고의 답변이어야합니다. 최소한의 놀라움의 원칙을 따르십시오. 비정상적인 작업을 수행하는 코드는 읽고 이해하기가 더 어렵고 코드는 작성된 것보다 훨씬 더 자주 읽 힙니다. 이런 귀여움 속임수를 사용하지 마세요.
Eloff

1
내 동료가 bool에 비트 "and"연산자를 사용했기 때문에 여기에 왔습니다. 그리고 지금은 이것이 옳은지 아닌지를 이해하려고 시간을 보내고 있습니다. 그가 이렇게하지 않았다면 나는 지금 더 유용한 것을 할 것입니다 -_-. 동료의 시간을 소중히 여기는 경우 bool에 비트 연산자를 사용하지 마십시오!
anton_rh dec.

9

비트 수준 연산자의 단점.

물어:

"는 비트 연산자 사용되지 않은 이유가 &, |그리고 ^C에서 부울"값 ++에 대한 "는? ”

예, 논리 연산자 이며, 높은 수준의 부울 연산자 내장 !, &&그리고 ||, 다음과 같은 장점을 제공합니다 :

  • 보증 인수의 변환bool에 즉, 01순서 값.

  • 최종 결과가 알려지 자마자 표현식 평가가 중지되는 단락 평가를 보장 합니다. True , FalseIndeterminate가
    있는 트리 값 논리로 해석 될 수 있습니다 .

  • 읽을 수있는 텍스트 등가물 not, andor, 내가 직접 사용하지 않더라도.
    댓글에 독자 안티몬 노트도 bitlevel 사업자가 다른 토큰을 가지고 즉 bitand, bitor, xorcompl,하지만 내 생각에이보다 적은 읽을 수 and, ornot.

간단히 말해서, 상위 수준 연산자의 이러한 각 장점은 비트 수준 연산자의 단점입니다.

특히 비트 연산자는 0/1 로의 인수 변환이 없기 때문에 예를 들어 1 & 20, 반면 1 && 2true. 또한 ^, 비트 배타적 또는이 방식으로 오작동 할 수 있습니다. 부울 값 1과 2는 동일 true하지만 비트 패턴으로 간주되어 서로 다릅니다.


논리적으로 표현하는 방법 / 또는 C ++에서.

그런 다음 질문에 대한 약간의 배경 정보를 제공하고

"가끔 두 가지 조건 중 정확히 하나가 참 (XOR)이되기를 원하는 상황이 발생하기 때문에 ^ 연산자를 조건식에 넣습니다."

음, 비트 연산자는 논리 연산자 보다 우선 순위높습니다 . 이것은 특히 다음과 같은 혼합 표현에서

a && b ^ c

아마도 예상치 못한 결과를 얻습니다. a && (b ^ c) .

대신 쓰기

(a && b) != c

의미를 더 간결하게 표현합니다.

다중 인수의 경우 / 또는 작업을 수행하는 C ++ 연산자가 없습니다. 당신이 쓰는 예를 들어, a ^ b ^ c보다 '중 하나라는 표현하지 a, b또는 c사실이다 ". 대신은 말한다, "의 홀수 a, bc 그들 또는 3의 1이 될 수있는, 참 ..."

일반 중 / 또는 표현하기 위해 a, b그리고 c유형입니다 bool, 단지 쓰기

(a + b + c) == 1

또는 bool인수 가 아닌 경우 다음으로 변환하십시오 bool.

(!!a + !!b + !!c) == 1


&=부울 결과를 누적하는 데 사용 합니다.

당신은 더 정교하게

"나는 때때로 부울 값을 축적해야하고, &=그리고 |=?매우 유용 할 수 있습니다."

글쎄, 이것은 각각 모든 조건이 충족 되는지 또는 어떤 조건이 충족 되는지 확인하는 것과 일치 하며 de Morgan의 법칙 은 하나에서 다른 것으로 이동하는 방법을 알려줍니다. 즉, 그중 하나만 필요합니다. 원칙적으로 사용할 수 *=있습니다.&&= 연산자 (조지 부울 (George Boole)이 발견 한 것처럼 논리적 AND는 매우 쉽게 곱셈으로 표현할 수 있음).하지만 나는 이것이 코드의 관리자를 혼란스럽고 오도 할 것이라고 생각합니다.

다음 사항도 고려하십시오.

struct Bool
{
    bool    value;

    void operator&=( bool const v ) { value = value && v; }
    operator bool() const { return value; }
};

#include <iostream>

int main()
{
    using namespace std;

    Bool a  = {true};
    a &= true || false;
    a &= 1234;
    cout << boolalpha << a << endl;

    bool b = {true};
    b &= true || false;
    b &= 1234;
    cout << boolalpha << b << endl;
}

Visual C ++ 11.0 및 g ++ 4.7.1을 사용한 출력 :

진실
그릇된

결과가 다른 이유는 비트 레벨 &=bool오른쪽 인수에 대한 변환을 제공하지 않기 때문입니다.

그렇다면이 결과 중 어떤 것을 사용하고 &=싶습니까?

전자가이면 true연산자 (예 : 위와 같이) 또는 명명 된 함수를 더 잘 정의하거나 오른쪽 표현식의 명시 적 변환을 사용하거나 전체 업데이트를 작성합니다.


비트 연산자도 텍스트 등가물
Antimony

@Antimony : 나는 처음에 그 설명을 이해하지 못했지만 예, bitlevel 작업은 다른 토큰을 가지고 bitand, bitor, xorcompl. 그래서 "읽기 쉬운"자격을 사용했다고 생각합니다. 물론 가독성 compl 42은 주관적입니다. ;-)
건배와 hth. - 알프

내가 말할 있는 한 논리 연산자 다른 인수 유형으로 오버로드 될 있으므로 (일반적으로 그렇지는 않지만) 첫 번째 요점 "인수를 다음으로 변환 보장 bool"은 C ++ 언어가 실제로 제공한다는 보장이 아니라 관례의 결과 일뿐입니다. 당신.
마크 반 리웬

@MarcvanLeeuwen : 시각적 하위 시스템이 "내장 된 고수준 부울 연산자!, && 및 ||"를 인식하지 못했을 수 있습니다. 이러한 연산자가 오버로드 될 수 있다는 것이 맞습니다. 그리고 주요 문제는 의미가 미묘하게 변경되고 더 이상 단락 평가가 아니라는 것입니다. 그러나 이는 내장 연산자가 아니라 과부하의 문제입니다.
건배와 hth. - 알프

맞습니다 (나는 그것을 놓쳤습니다). 그러나이 문장은 여전히 ​​오해의 소지가 있습니다. 왜냐하면 여러분은 그 연산자가 부울 인수를 취한다고 말하고 있기 때문입니다. 연산자의 내장 버전이 선택되면 컴파일러는 인수 변환을 삽입합니다. 마찬가지로 (하이 레벨)의 부호없는 정수 가산 연산자 내장 +하는 인수의 전환을 보장 unsigned하지만, 그 방해하지 않는 unsigned int n=-1; n+=3.14;사실을 사용하는 double(또는 인 float?) 또한 동작. (당신의 비교 &=예.)
마크 반 리웬에게

3

Patrick의 대답과는 달리 C ++에는 ^^단락 독점 또는 수행을위한 연산자 가 없습니다 . 잠시 생각해 보면 ^^연산자를 갖는 것은 어차피 의미가 없을 것입니다. 또는 배타적 또는 결과는 항상 두 피연산자에 따라 달라집니다. 그러나, 비에 대한 패트릭의 경고 bool비교할 때 "부울"유형은 동일하게 유지 1 & 21 && 2. 이에 대한 한 가지 전형적인 예는 Windows GetMessage()함수로, BOOL0이 아닌 0, 또는 -1.

&대신 &&|대신 사용 ||하는 것은 드문 오타가 아니므로 의도적으로 사용하는 경우 이유를 설명하는 주석이 필요합니다.


6
^^연산자없이 여전히 단락 고려 유용하다. a) 부울 컨텍스트에서 피연산자를 평가하고 b) 1 또는 0을 반환하도록 보장합니다.
Craig McQueen

@CraigMcQueen. inline bool XOR(bool a,bool b) { return a!=b; }(중위) 연산자가 아니라 함수라는 점을 제외하면 원하는 것을 정의 하고 얻는 것은 간단합니다 . 또는 !=이 의미로 다른 연산자를 직접 사용 하거나 오버로드 할 수 있지만 물론 실수로 동일한 이름의 의도하지 않은 오버로드를 사용하지 않도록 매우주의해야합니다. 그리고 우연히 ||&&반환 true하거나 false,하지 10.
마크 반 리웬

그리고 부울 컨텍스트에서 !, ||&&인수를 평가하는 것은 이러한 연산자가 다른 유형을 허용하기 위해 오버로드되는 경우가 거의 없기 때문인 것 같습니다 . 내가 말할 수있는 한 언어는 그러한 과부하를 허용하므로 부울 컨텍스트에서 인수 평가를 보장하지 않습니다.
마크 반 리웬

2

패트릭은 좋은 지적을했고 나는 그것을 반복하지 않을 것입니다. 그러나 이름이 잘 지정된 부울 변수를 사용하여 가능하면 'if'문을 읽을 수있는 영어로 줄이는 것이 좋습니다.

bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
 .. stuff ..
}

부울을 사용하는 것이 불필요하다고 생각할 수 있지만 두 가지 주요 사항에 도움이됩니다.

  • 'if'조건에 대한 중간 부울은 조건의 의도를 더 명확하게 만들기 때문에 코드를 이해하기 더 쉽습니다.
  • 부울 값에 비트 연산자와 같은 비표준 또는 예상치 못한 코드를 사용하는 경우 사람들은이 작업을 수행 한 이유를 훨씬 더 쉽게 알 수 있습니다.

편집 : 당신은 'if'문에 대한 조건문을 원한다고 명시 적으로 말하지 않았습니다 (가장 가능성이 높지만), 그것은 내 가정이었습니다. 그러나 중간 부울 값에 대한 나의 제안은 여전히 ​​유효합니다.


1

bool에 비트 연산을 사용하면 논리 연산에서 가져온 'cmp'명령으로 인해 프로세서에서 불필요한 분기 예측 논리를 절약 할 수 있습니다.

논리를 비트 연산 (모든 피연산자가 bool 인 경우)으로 대체하면 동일한 결과를 제공하는보다 효율적인 코드가 생성됩니다. 논리 연산을 사용하여 주문할 때 활용할 수있는 모든 단락 이점보다 효율성이 이상적으로 중요합니다.

프로그래머가 그 이유를 주석으로 처리해야하지만 코드를 읽을 수 없게 만들 수 있습니다.


0

IIRC, 많은 C ++ 컴파일러는 비트 연산의 결과를 bool로 캐스트하려고 할 때 경고합니다. 컴파일러를 행복하게 만들려면 타입 캐스트를 사용해야합니다.

if 표현식에서 비트 연산을 사용하면 컴파일러가 아닌 경우에도 동일한 비판을받을 수 있습니다. 0이 아닌 값은 모두 참으로 간주되므로 "if (7 & 3)"과 같은 것이 참이됩니다. 이 동작은 Perl에서 허용 될 수 있지만 C / C ++는 매우 명시적인 언어입니다. 스팍 눈썹은 실사라고 생각합니다. :) "== 0"또는 "! = 0"을 추가하여 귀하의 목표가 무엇인지 완벽하게 명확히합니다.

그러나 어쨌든 그것은 개인적인 선호처럼 들립니다. Lint 또는 유사한 도구를 통해 코드를 실행하고 그것이 현명하지 못한 전략이라고 생각하는지 확인합니다. 개인적으로 그것은 코딩 실수처럼 읽습니다.


귀하의 게시물은 나를 테스트하도록 동기를 부여했으며 gcc는 경고하지 않았습니다! Bummer, 나는 그 경고를 사용하여 bool 결과를 축적하기 위해 & =를 실행하는 것을 정당화하려고했기 때문에 다른 사람들이 나중에 내 테스트를 변경하면 경고를 볼 것이라고 믿었습니다. 내 코드는 경고없이 0 / false로 평가되기 때문에 짝수 정수에 대해서도 실패합니다! 리팩토링 할 시간 ...
sage
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.