모든 비트를 true로 설정하기 위해 -1을 사용하는 것이 안전합니까?

StackOverflow https://stackoverflow.com/questions/809227

  •  03-07-2019
  •  | 
  •  

문제

이 패턴이 C & C ++에서 많이 사용되는 것을 보았습니다.

unsigned int flags = -1;  // all bits are true

이것이 이것을 달성하는 좋은 휴대용 방법입니까? 또는 사용 중입니다 0xffffffff 또는 ~0 더 나은?

도움이 되었습니까?

해결책

가장 간단한 것이기 때문에 당신이 보여준대로 정확하게 할 것을 권장합니다. 초기화 -1 작동 할 것입니다 언제나, 실제 부호 표현과 무관합니다 ~ 올바른 피연산자 유형이 있어야하기 때문에 때때로 놀라운 행동을 취합니다. 그래야만 당신은 가장 높은 가치를 얻을 것입니다. unsigned 유형.

놀라운 놀라움의 예를 들어, 이것을 고려하십시오.

unsigned long a = ~0u;

반드시 모든 비트 1이있는 패턴을 a. 그러나 먼저 모든 비트 1이있는 패턴을 만듭니다. unsigned int, 그런 다음 할당하십시오 a. 언제 일어날 지 unsigned long 더 많은 비트가 있습니다. 모든 것이 모두 1이 아니라는 것입니다.

그리고 두 가지가 아닌 보완 대표에서 실패 할 것입니다.

unsigned int a = ~0; // Should have done ~0u !

그 이유는 그 이유입니다 ~0 모든 비트를 뒤집어 야합니다. 그 결과 반전 -1 둘의 보완 기계에서 (우리가 필요로하는 가치입니다!) ~ 아니다 생산하다 -1 다른 표현에. 보완 기계에서는 0이됩니다. 따라서 보완 기계에서 위의는 초기화됩니다. a 0으로.

당신이 이해해야 할 것은 그것이 비트가 아닌 가치에 관한 것입니다. 변수는 a로 초기화됩니다 . 이니셜 라이저에서 초기화에 사용되는 변수의 비트를 수정하면 해당 비트에 따라 값이 생성됩니다. 초기화하는 데 필요한 값 a 가능한 가장 높은 가치는입니다 -1 또는 UINT_MAX. 두 번째는 유형에 따라 다릅니다 a - 사용해야합니다 ULONG_MAX 용을 위해 unsigned long. 그러나 첫 번째는 그 유형에 의존하지 않으며 가장 높은 가치를 얻는 좋은 방법입니다.

우리는 ~ 아니다 여부에 대해 이야기합니다 -1 모든 비트가 있습니다 (항상 가지고있는 것은 아닙니다). 그리고 우리는 ~ 아니다 여부에 대해 이야기합니다 ~0 모든 비트가 있습니다 (물론).

하지만 우리가 말하는 것은 초기화 된 결과입니다. flags 변수입니다. 그리고 그것을 위해 -1 모든 유형과 기계와 함께 작동합니다.

다른 팁

  • unsigned int flags = -1; 휴대용입니다.
  • unsigned int flags = ~0; 두 개의 보수 표현에 의존하기 때문에 휴대용이 아닙니다.
  • unsigned int flags = 0xffffffff; 32 비트 INT를 가정하기 때문에 휴대용이 아닙니다.

C 표준이 보장하는 방식으로 모든 비트를 설정하려면 첫 번째 비트를 사용하십시오.

솔직히 나는 모든 FFF가 더 읽기 쉽다고 생각합니다. 반포 란드라는 의견에 관해서는, 당신이 모든 비트가 설정/청소되었다고 정말로 관심이 있다면, 나는 당신이 어쨌든 변수의 크기에 관심이있는 상황에 있다고 주장 할 것입니다. :: uint16_t 등

언급 된 문제를 피하는 방법은 단순히 다음과 같습니다.

unsigned int flags = 0;
flags = ~flags;

휴대용과 요점까지.

플래그에 서명되지 않은 int를 사용하는 것이 C ++에서 처음에는 좋은 아이디어인지 확실하지 않습니다. 비트 셋 등은 어떻습니까?

std::numeric_limit<unsigned int>::max() 더 낫기 때문입니다 0xffffffff 서명되지 않은 INT는 32 비트 정수라고 가정합니다.

unsigned int flags = -1;  // all bits are true

"이것은 이것을 달성하는 좋은 휴대용 방법입니까?"

가지고 다닐 수 있는? .

좋은? 논쟁의 여지가 있습니다,이 스레드에 표시된 모든 혼란에 의해 입증 된 바와 같이. 동료 프로그래머가 혼란없이 코드를 이해할 수있을 정도로 분명하다면 좋은 코드를 위해 우리가 측정하는 차원 중 하나가되어야합니다.

또한이 방법은 발생하기 쉽습니다 컴파일러 경고. 컴파일러를 무너 뜨리지 않고 경고를 피하려면 명백한 캐스트가 필요합니다. 예를 들어,

unsigned int flags = static_cast<unsigned int>(-1);

명시 적 캐스트는 대상 유형에주의를 기울여야합니다. 대상 유형에주의를 기울이면 자연스럽게 다른 접근법의 함정을 피할 수 있습니다.

저의 조언은 대상 유형에주의를 기울이고 암시 적 전환이 없는지 확인하는 것입니다. 예를 들어:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

모두입니다 정확하고 더 분명합니다 동료 프로그래머에게.

그리고 C ++ 11: 우리는 사용할 수 있습니다 auto 이것들을 더욱 단순하게 만들기 위해 :

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

나는 단순히 교정하는 것보다 정확하고 명백하다고 생각합니다.

서명되지 않은 유형으로 -1을 변환합니다 표준에 의해 보장됩니다 모든 일을 초래합니다. 사용 ~0U 이후 일반적으로 나쁘다 0 유형이 있습니다 unsigned int 명시 적으로 다음과 같은 글을 쓰지 않는 한 더 큰 서명되지 않은 유형의 모든 비트를 채우지 않습니다. ~0ULL. Sane Systems에서 ~0 동일해야합니다 -1, 그러나 표준은 하나의 압수 및 부호/크기 표현을 허용하기 때문에 엄격하게 말하면 휴대가 가능하지 않습니다.

물론 글을 쓰는 것은 항상 괜찮습니다 0xffffffff 정확히 32 비트가 필요하지만 -1은 여러 유형에서 작동하는 매크로와 같은 유형의 크기를 모르거나 유형의 크기를 모르는 경우에도 어떤 맥락에서도 작동한다는 장점이 있습니다. 구현에 따라 다릅니다. 유형을 알고 있다면 All-one을 얻는 또 다른 안전한 방법은 한계 매크로입니다. UINT_MAX, ULONG_MAX, ULLONG_MAX, 등.

개인적으로 나는 항상 -1을 사용합니다. 그것은 항상 작동하며 그것에 대해 생각할 필요가 없습니다.

예. 다른 답변에서 언급했듯이 -1 가장 휴대용입니다. 그러나 그것은 의미 론적이지 않으며 컴파일러 경고를 유발합니다.

이러한 문제를 해결하려면이 간단한 도우미를 사용해보십시오.

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

용법:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;

당신이 가진 한 #include <limits.h> 포함 중 하나 로서만 사용해야합니다.

unsigned int flags = UINT_MAX;

Long의 가치를 원한다면 사용할 수 있습니다.

unsigned long flags = ULONG_MAX;

이 값은 서명 된 정수가 구현되는 방법에 관계없이 결과의 모든 값 비트를 1로 설정합니다.

나는 -1 일을하지 않을 것이다. 다소 직관적이지 않습니다 (적어도 나에게). 서명되지 않은 변수에 서명 된 데이터를 할당하는 것은 자연적인 사물의 순서를 위반 한 것 같습니다.

당신의 상황에서 나는 항상 사용합니다 0xFFFF. (물론 변수 크기에 적합한 FS를 사용하십시오.)

BTW, 실제 코드에서 -1 트릭을 거의 볼 수 없습니다.

또한 Vairable의 개별 비트에 대해 정말로 관심이 있다면 고정 된 width를 사용하는 것이 좋습니다. uint8_t, uint16_t, uint32_t 유형.

인텔의 IA-32 프로세서에서는 0xffffffff를 64 비트 레지스터에 작성하고 예상 결과를 얻는 것이 좋습니다. IA32E (IA32에 대한 64 비트 확장)는 32 비트 즉각적인 즉시 지원하기 때문입니다. 64 비트 지침에서 32 비트 즉시 즉시 사인 확장 64 비트까지.

다음은 불법입니다.

mov rax, 0ffffffffffffffffh

다음은 RAX에 64 1을 넣습니다.

mov rax, 0ffffffffh

완전성을 위해 다음은 RAX (일명 EAX)의 하단에 32 1을 넣습니다.

mov eax, 0ffffffffh

그리고 사실 나는 0xffffffff를 64 비트 변수에 쓰고 싶을 때 프로그램이 실패했으며 대신 0xffffffffffffffff를 얻었습니다. C에서 이것은 다음과 같습니다.

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

결과는 다음과 같습니다.

x is 0xffffffffffffffff

나는 0xffffffff가 32 비트를 가정한다고 말한 모든 답변에 대해 의견으로 게시 할 것이라고 생각했지만, 많은 사람들이 대답했다고 생각했다. 나는 그것을 별도의 대답으로 추가 할 것이라고 생각했다.

문제에 대한 명확한 설명은 Litb의 답변을 참조하십시오.

저의 의견 불일치는 매우 엄격하게 말하면 두 경우 모두에 대한 보장이 없다는 것입니다. 나는 모든 비트가 세워진 것처럼 서명되지 않은 값 '1보다 2 미만의 힘의 힘'의 값을 나타내지 않는 아키텍처를 모르지만, 표준이 실제로 말하는 내용은 다음과 같습니다 (3.9.1/7 Plus 참고 44) :

적분 유형의 표현은 순수한 바이너리 분수 시스템을 사용하여 값을 정의해야합니다. [참고 44 :] 이진 숫자 0 및 1을 사용하는 정수에 대한 위치 표현은 연속 비트로 표시되는 값이 부가가치이며 1로 시작하며 비트를 제외하고 2의 연속 적분 전력을 곱합니다. 가장 높은 위치.

그것은 비트 중 하나가 전혀 없을 가능성을 남깁니다.

실제로 : 그렇습니다

이론적으로 : 아니요.

-1 = 0xffffffff (또는 int가 플랫폼에있는 크기)는 2의 보완 산술에서만 사실입니다. 실제로는 작동하지만 2의 보완 표현이 아닌 실제 부호 비트가있는 레거시 머신 (IBM 메인 프레임 등)이 있습니다. 제안 된 ~ 0 솔루션은 어디에서나 작동해야합니다.

비록 0xFFFF (또는 0xFFFFFFFF, 등을 읽기가 더 쉬울 수 있으며, 그렇지 않으면 휴대 할 수있는 코드의 휴대 성을 깨뜨릴 수 있습니다. 예를 들어, 라이브러리 루틴을 고려하십시오. 데이터 구조의 특정 비트 세트 (발신자가 지정한 정확한 비트)가있는 항목 수를 계산하는 것을 고려하십시오. 루틴은 비트가 나타내는 것에 대해 완전히 불가지론적일 수 있지만 여전히 "모든 비트 세트"가 일정해야합니다. 이러한 경우 -1은 비트 크기로 작동하기 때문에 16 진수보다 훨씬 우수합니다.

다른 가능성은 a typedef 값은 비트 마스크에 사용되며 ~ (bitmasktype) 0을 사용하는 것입니다. 비트 마스크가 16 비트 유형으로 만 발생하면, 그 표현식은 16 비트 세트 만 있습니다 ( 'int'는 32 비트 인 경우에도 16 비트가 필요한 모든 것이기 때문에 상황이 좋을 것입니다. 제공 그것은 실제로 TypeCast에서 실제로 적절한 유형을 사용합니다.

또한, 형태의 표현 longvar &= ~[hex_constant] 육각 상수가 너무 커서 int, 그러나에 맞습니다 unsigned int. 만약 int 그러면 16 비트입니다 longvar &= ~0x4000; 또는 longvar &= ~0x10000; 하나의 약간을 지울 것입니다 longvar, 하지만 longvar &= ~0x8000; 비트 15와 그 위의 모든 비트를 제거합니다. 적합한 값 int 보완 연산자가 유형에 적용됩니다 int, 그러나 결과는 서명으로 확장됩니다 long, 상단 비트를 설정합니다. 너무 큰 값 unsigned int 보완 연산자가 유형에 적용됩니다 long. 그러나 해당 크기 사이에있는 값은 보완 연산자를 유형에 적용합니다. unsigned int, 그런 다음 유형으로 변환됩니다 long 표시 연장없이.

다른 사람들이 언급했듯이 -1은 모든 비트가 1으로 설정된 서명되지 않은 유형으로 변환하는 정수를 만드는 올바른 방법입니다. 그러나 C ++에서 가장 중요한 것은 올바른 유형을 사용하는 것입니다. 따라서 문제에 대한 정답 (질문에 대한 답변 포함)은 다음과 같습니다.

std::bitset<32> const flags(-1);

여기에는 항상 필요한 정확한 비트가 포함됩니다. 그것은 구성 a std::bitset 다른 답변에 언급 된 것과 같은 이유로 모든 비트가 1으로 설정되어 있습니다.

-1에는 항상 사용 가능한 비트 세트가 있기 때문에 확실히 안전하지만 ~ 0을 더 좋아합니다. -1은 그냥 의미가 없습니다 unsigned int. 0xFF... 유형의 너비에 따라 다르기 때문에 좋지 않습니다.

내가 말하다:

int x;
memset(&x, 0xFF, sizeof(int));

이것은 항상 당신에게 원하는 결과를 줄 것입니다.

서명되지 않은 유형에 대한 모든 비트를 하나에 할당하는 것이 주어진 유형의 가능한 최대 값을 취하는 것과 같습니다.
질문의 범위를 모두에게 확장합니다 서명되지 않았습니다 정수 유형 :

-1 할당은 무엇보다도 작동합니다 서명되지 않았습니다 정수 유형 C 및 C ++ 모두에 대한 (부호없는 int, uint8_t, uint16_t 등).

대안으로 C ++의 경우 다음 중 하나를 사용할 수 있습니다.

  1. 포함 <limits> 그리고 사용 std::numeric_limits< your_type >::max()
  2. a 사용자 정의 템플릿 기능 (이것은 또한 목적지 유형이 실제로 서명되지 않은 유형 인 경우 일부 정신 점검을 허용합니다)

할당함에 따라 목적은 더 명확성을 더할 수 있습니다. -1 항상 설명적인 의견이 필요합니다.

의미를 조금 더 명확하게 만들지 만 유형을 반복하지 않는 방법 :

const auto flags = static_cast<unsigned int>(-1);

예, 표시된 표현은 우리가 다른 방식으로 작업하는 것처럼 매우 정확합니다.

예를 들어 대부분의 기계에서 정수는 2 바이트 = 16 비트입니다.

0%65536 = 0-1%65536 = 65535로 송로 폰드가 1111 ............. 1 및 모든 비트가 1으로 설정되어 있습니다 (잔류 물 클래스 MOD 65536을 고려하면) 이에 따라 큽니다. 곧바로.

나는 추측한다

아니오 당신 이이 개념을 고려한다면 그것은 서명되지 않은 int를 위해 완벽하게 식사를하고 실제로는 작동합니다.

다음 프로그램 조각을 확인하십시오

int main () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

B = 4294967295에 대한 답변 WHCIH는 4 바이트 정수에서 -1%2^32입니다.

따라서 서명되지 않은 정수에 완벽하게 유효합니다

불일치의 경우 PLZZ 보고서

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top