C 및 C ++에서 부호없는 정수 사용


23

나는 오랫동안 나를 방해하는 매우 간단한 질문이 있습니다. 나는 네트워크와 데이터베이스를 다루고 있으므로 처리중인 많은 데이터는 32 비트 및 64 비트 카운터 (서명되지 않음), 32 비트 및 64 비트 식별 ID (서명에 대한 의미있는 매핑이 없음)입니다. 나는 실제로 음수로 표현 될 수있는 실제 단어 문제를 다루지 않습니다.

나와 내 동료들은 정기적으로 같은 부호없는 형식을 사용 uint32_t하고 uint64_t이 문제에 대한 그리고 너무 자주 발생하기 때문에 우리는 또한 배열 인덱스 및 기타 일반적인 정수 용도를 사용합니다.

동시에 다양한 코딩 안내서 (예 : Google)는 부호없는 정수 유형의 사용을 권장하지 않으며 Java 또는 Scala에 부호없는 정수 유형이 없다는 것을 알고 있습니다.

따라서 올바른 일이 무엇인지 알 수 없었습니다. 환경에서 부호있는 값을 사용하는 것은 매우 불편한 동시에 코딩 가이드 가이 작업을 정확하게 수행하도록 요구하는 것은 매우 불편합니다.


답변:


31

이것에 대해 두 개의 사고 학교가 있으며, 어느 쪽도 동의하지 않을 것입니다.

첫 번째는 배열 인덱스와 같이 본질적으로 서명되지 않은 몇 가지 개념이 있다고 주장합니다. 오류가 발생할 수 있으므로 부호있는 숫자를 사용하는 것은 의미가 없습니다. 또한 부호없는 32 비트 인덱스를 사용하는 배열은 20 억 항목에만 액세스 할 수있는 반면 부호없는 32 비트 숫자로 전환하면 40 억 항목이 허용됩니다.

두 번째는 부호없는 숫자를 사용하는 모든 프로그램에서 조만간 부호없는 부호가 혼합 된 산술을 할 것이라고 주장합니다. 이는 부호없는 값을 부호로 캐스팅하면 음수를, 반대로 부호를 부호없는 것으로 캐스팅하면 큰 양수를 생성합니다. 이것은 큰 오류의 원인이 될 수 있습니다.


8
부호없는 혼합 산술 문제는 컴파일러에서 감지됩니다. 빌드에 경고가없는 상태로 유지하십시오 (경고 수준이 높음). 게다가, int유형이 짧습니다 :)
rucamzu

7
고백 : 저는 두 번째 생각을하고 있지만 서명되지 않은 유형에 대한 고려 사항을 이해하지만 int99.99 %의 배열 인덱스에 충분합니다. 부호있는 부호없는 산술 문제가 훨씬 흔하므로 피해야 할 사항이 우선합니다. 그렇습니다. 컴파일러는 이것에 대해 경고하지만, 규모가 큰 프로젝트를 컴파일 할 때 얼마나 많은 경고가 표시됩니까? 경고를 무시하는 것은 위험하고 나쁜 습관이지만 실제 세계에서는 ...
Elias Van Ootegem

11
답변 +1 주의 : Blunt Opinions Ahead : 1 : 제 2의 사고 방식에 대한 저의 반응은 : C에서 부호없는 정수 유형에서 예기치 않은 결과를 얻는 사람 은 정의되지 않은 행동 (순수한 학문적 종류는 아님)을 가질 것이라고 내기 했습니다. 부호있는 정수 유형 을 사용하는 사소한 C 프로그램 부호없는 유형이 더 나은 유형이라고 생각하기에 C를 잘 모르는 경우 C를 피하는 것이 좋습니다 .2 : C에는 배열 인덱스와 크기에 대해 정확히 하나의 유형 size_t이 있으며, 특별한 경우가 아니면 그렇지 않으면 좋은 이유.
mtraceur

5
혼합 된 서명없이 문제가 발생합니다. 부호없는 int에서 부호없는 int를 뺀 값을 계산하십시오.
gnasher729

4
단지와 시몬 당신과 함께 문제를 복용하지 생각의 첫번째 학교 있다고 주장한다 "본질적으로 서명되지 않은 몇 가지 개념이있다 -. 이러한 배열의 인덱스로는" 특히 "배열 인덱스에 대한 단 하나의 올바른 유형은 ... C에있다" 헛소리! . 우리 DSPer는 항상 음의 지수를 사용합니다. 특히 원인이 아닌 짝수 또는 홀수 대칭 임펄스 응답의 경우. LUT 수학을 위해. 나는 두 번째 생각의 학교에 있지만 C 및 C ++에서 부호있는 정수와 부호없는 정수를 모두 갖는 것이 유용하다고 생각합니다 .
robert bristow-johnson 1

21

우선, Google C ++ 코딩 가이드 라인은 따라야 할 것이 좋지 않습니다. 현대 C ++의 기본 요소 인 예외, 부스트 등을 피합니다. 둘째, 특정 가이드 라인이 회사 X에 효과적이라고해서 그것이 귀하에게 꼭 맞는다는 의미는 아닙니다. 나는 당신이 그들을 필요로하기 때문에 서명되지 않은 유형을 계속 사용할 것입니다.

C ++의 적절한 경험 법칙은 int다른 것을 사용해야 할 이유가없는 한 선호 하는 것입니다.


8
그것은 내가 전혀 의미하는 것이 아닙니다. 생성자는 불변 값을 설정하기위한 것이며, 함수가 아니기 때문에 return false불변 값이 설정 되지 않으면 간단하게 할 수 없습니다 . 따라서 객체를 분리하고 객체에 init 함수를 사용하거나을 던지고 std::runtime_error스택 해제가 발생하도록하고 모든 RAII 객체가 자동으로 정리되도록하고 개발자가 편리한 곳에서 예외를 처리 할 수 ​​있습니다 당신은 그렇게합니다.
bstamour

5
응용 프로그램 유형이 어떻게 다른지 알 수 없습니다. 객체에서 생성자를 호출 할 때마다 매개 변수를 사용하여 불변을 설정합니다. 그 불변이 충족되지 않으면, 오류 신호를 보내야합니다. 그렇지 않으면 프로그램이 양호한 상태가 아닙니다. 생성자는 플래그를 반환 할 수 없으므로 예외를 던지는 것은 자연스러운 옵션입니다. 비즈니스 애플리케이션이 이러한 코딩 스타일의 이점을 얻지 못하는 이유에 대해 확실한 주장을하십시오.
bstamour

8
모든 C ++ 프로그래머의 절반이 예외를 올바르게 사용할 수 없다는 것이 매우 의심 스럽다. 그러나 어쨌든 동료가 현대 C ++을 작성할 수 없다고 생각한다면 반드시 현대 C ++에서 멀리하십시오.
bstamour

6
@ zzz777 예외를 사용하지 않습니까? 공개 팩토리 함수에 의해 래퍼되는 개인 생성자가 예외를 포착하고 무엇을 반환 nullptr합니까? "기본"객체를 반환합니까? 당신은 아무것도 해결하지 못했습니다-당신은 카펫 아래에 문제를 숨기고 아무도 발견하지 않기를 바랍니다.
Mael

5
@ zzz777 어쨌든 상자가 충돌 할 경우 왜 예외에서 발생하는지 걱정 signal(6)합니까? 예외를 사용하면 처리 방법을 알고있는 개발자의 50 %가 좋은 코드를 작성할 수 있으며 나머지는 동료가 수행 할 수 있습니다.
IllusiveBrian

6

다른 답변에는 실제 예제가 없기 때문에 하나를 추가하겠습니다. 내가 (개인적으로) 서명되지 않은 유형을 피하려고하는 이유 중 하나입니다.

표준 size_t를 배열 인덱스로 사용하십시오.

for (size_t i = 0; i < n; ++i)
    // do something here;

좋아, 완벽하게 정상이야 그런 다음 어떤 이유로 루프 방향을 변경하기로 결정했습니다.

for (size_t i = n - 1; i >= 0; --i)
    // do something here;

그리고 지금은 작동하지 않습니다. int반복자로 사용하면 아무런 문제가 없습니다. 지난 2 년 동안 이러한 오류가 두 번 나타났습니다. 일단 프로덕션 환경에서 발생하고 디버깅하기가 어려웠습니다.

나를위한 또 다른 이유는 성가신 경고이며, 매번 이런 식으로 글을 쓰게합니다 .

int n = 123;  // for some reason n is signed
...
for (size_t i = 0; i < size_t(n); ++i)

이것들은 사소한 것이지만 합산합니다. 부호있는 정수 만 어디에서나 사용하면 코드가 깨끗하다고 ​​생각합니다.

편집 : 물론, 예제는 바보처럼 보이지만 사람들 이이 실수를하는 것을 보았습니다. 그것을 피하는 쉬운 방법이 있다면, 왜 그것을 사용하지 않습니까?

VS2015 또는 GCC로 다음 코드를 컴파일하면 기본 경고 설정 (GCC의 경우 -Wall을 사용하더라도)에 대한 경고가 표시되지 않습니다. GCC에서 이에 대한 경고를 받으려면 -Wextra를 요청해야합니다. 이것이 항상 Wall 및 Wextra로 컴파일해야하고 정적 분석기를 사용해야하는 이유 중 하나이지만, 많은 실제 프로젝트에서 사람들은 그렇게하지 않습니다.

#include <vector>
#include <iostream>


void unsignedTest()
{
    std::vector<int> v{ 1, 2 };

    for (int i = v.size() - 1; i >= 0; --i)
        std::cout << v[i] << std::endl;

    for (size_t i = v.size() - 1; i >= 0; --i)
        std::cout << v[i] << std::endl;
}

int main()
{
    unsignedTest();
    return 0;
}

부호있는 유형으로 더 잘못 될 수 있습니다 ... 그리고 예제 코드는 너무 죽어서 눈에 띄게 잘못되어 경고를 요구하면 괜찮은 컴파일러가 경고합니다.
중복 제거기

1
나는 과거에 for (size_t i = n - 1; i < n; --i)그것이 제대로 작동 하도록 그런 공포에 의지했다 .
Simon B

2
for-loops에 대해 size_t반대로 말하면 , 다음과 같은 스타일의 코딩 지침이 있습니다.for (size_t revind = 0u; revind < n; ++revind) { size_t ind = n - 1u - revind; func(ind); }
rwong

2
@ rwong Omg, 이것은 못생긴입니다. 왜 사용하지 int않습니까? :)
Aleksei Petrenko

1
@AlexeyPetrenko – 현재의 C 또는 C ++ 표준 int은 모든 유효한 값을 보유 할만큼 충분히 큰 것을 보증하지 않습니다 size_t. 특히, int최대 2 ^ 15-1까지의 숫자 만 허용 할 수 있으며, 일반적으로 메모리 할당 제한이 2 ^ 16 (또는 경우에 따라 더 높음) 인 시스템에서 가능합니다. long여전히 작동 한다고 보장 되지는 않지만 더 안전한 내기 일 수 있습니다 . 만 size_t모든 플랫폼에서 모든 경우에 작동을 보장한다.
Jules

4
for (size_t i = v.size() - 1; i >= 0; --i)
   std::cout << v[i] << std::endl;

여기서 문제는 루프를 잘못 동작하여 잘못된 동작을 유발한다는 것입니다. 루프의 구성은 초보자가 서명 된 유형 (정확하고 올바른)에 대해 배우는 것과 같지만 서명되지 않은 값에는 적합하지 않습니다. 그러나 이것은 서명되지 않은 유형을 사용하는 것에 대한 반론으로 작용할 수는 없습니다. 여기서 과제는 단순히 루프를 올바르게 얻는 것입니다. 그리고 이것은 다음 과 같이 서명되지 않은 유형에 안정적으로 작동하도록 쉽게 고칠 수 있습니다 .

for (size_t i = v.size(); i-- > 0; )
    std::cout << v[i] << std::endl;

이 변경은 단순히 비교 및 ​​감소 작업의 순서를 되돌리고 내 의견으로는 서명되지 않은 카운터를 역방향 루프로 처리하는 가장 효과적이고 방해받지 않고 깨끗하며 단축 방법이라고 생각합니다. while 루프를 사용할 때 (직관적으로) 똑같은 일을 할 것입니다.

size_t i = v.size();
while (i > 0)
{
    --i;
    std::cout << v[i] << std::endl;
}

언더 플로우가 발생하지 않으며, 서명 된 카운터 루프의 잘 알려진 변형에서와 같이 빈 컨테이너의 경우는 암시 적으로 커버되며 루프 본문은 서명 된 카운터 또는 순방향 루프와 비교하여 변경되지 않은 채로있을 수 있습니다. 처음에는 다소 이상한 모양의 루프 구조에 익숙해 져야합니다. 그러나 당신이 12 번을 본 후에는 더 이상 이해할 수없는 것이 없습니다.

초보자 코스가 부호있는 유형뿐만 아니라 부호없는 유형에 대한 올바른 루프를 표시하면 운이 좋을 것입니다. 이것은 서명되지 않은 유형을 비난하는 대신 IMHO가 무의식 개발자에게 비난 해야하는 몇 가지 오류를 피할 수 있습니다.

HTH


1

부호없는 정수가 이유가 있습니다.

예를 들어, 데이터를 개별 바이트로 처리하는 것을 고려하십시오 (예 : 네트워크 패킷 또는 파일 버퍼). 때때로 24 비트 정수와 같은 짐승이 발생할 수 있습니다. 3 개의 8 비트 부호없는 정수에서 쉽게 비트 시프트되며 8 비트 부호있는 정수로는 쉽지 않습니다.

또는 문자 찾아보기 테이블을 사용하는 알고리즘에 대해 생각해보십시오. 문자가 8 비트 부호없는 정수인 경우 문자 값으로 찾아보기 테이블을 색인화 할 수 있습니다. 그러나 프로그래밍 언어가 부호없는 정수를 지원하지 않으면 어떻게해야합니까? 배열에 음수 인덱스가 있습니다. 글쎄, 당신이 같은 것을 사용할 수 있다고 생각 charval + 128하지만 그건 못생긴 것입니다.

실제로 많은 파일 형식은 부호없는 정수를 사용하며 응용 프로그래밍 언어가 부호없는 정수를 지원하지 않으면 문제가 될 수 있습니다.

그런 다음 TCP 시퀀스 번호를 고려하십시오. TCP 처리 코드를 작성하는 경우 부호없는 정수를 사용해야합니다.

때로는 효율성이 너무 중요하여 부호없는 정수의 여분 비트가 실제로 필요합니다. 예를 들어 수백만으로 출하되는 IoT 장치를 고려하십시오. 그런 다음 마이크로 최적화에 많은 프로그래밍 리소스를 사용할 수 있습니다.

나는 부호없는 정수 유형 (혼합 부호 산술, 혼합 부호 비교)을 사용하지 않는 정당화는 적절한 경고가있는 컴파일러에 의해 극복 될 수 있다고 주장합니다. 이러한 경고는 일반적으로 기본적으로 활성화되어 있지 않지만 예를 들어 -Wextra또는 별도로 -Wsign-compare( -WextraC ++에서는 자동 활성화되지는 않지만 C by 에서 자동 활성화) 및을 참조하십시오 -Wsign-conversion.

그럼에도 불구하고 확실하지 않은 경우 서명 된 유형을 사용하십시오. 여러 번, 그것은 잘 작동하는 선택입니다. 그리고 컴파일러 경고를 활성화하십시오!


0

정수가 실제로 숫자를 나타내지 않지만 비트 마스크, id 등과 같은 많은 경우가 있습니다. 기본적으로 정수에 1을 추가해도 의미있는 결과가 없습니다. 이 경우 부호없는 것을 사용하십시오.

정수로 산술하는 경우가 많이 있습니다. 이러한 경우 부호없는 정수를 사용하여 0 부근의 오작동을 피하십시오. 루프를 0으로 낮추면 매우 직관적이지 않은 코드를 사용하거나 부호없는 숫자를 사용하여 중단되는 루프의 많은 예제를 참조하십시오. "하지만 지수는 절대 음수는 아닙니다"라는 주장이 있습니다. 물론 지수의 차이는 음수입니다.

인덱스가 2 ^ 31을 초과하지만 2 ^ 32를 초과하지 않는 매우 드문 경우에는 부호없는 정수를 사용하지 않고 64 비트 정수를 사용합니다.

마지막으로, 좋은 함정 : 루프에서 "for (i = 0; i <n; ++ i) a [i] ..."i 부호없는 32 비트이고 메모리가 32 비트 주소를 초과하면 컴파일러에서 최적화 할 수 없습니다 i = 2 ^ 32-1에서 나는 랩을하기 때문에 포인터를 증가시킴으로써 [i]에 대한 액세스. n이 절대 그렇게 커지지 않더라도. 부호있는 정수를 사용하면 이것을 피할 수 있습니다.


-5

마지막으로 J.Viega와 M.Messier의 "Secure Programming Cookbook"( http://shop.oreilly.com/product/9780596003944.do ) 에서 정말 좋은 답변을 찾았습니다.

부호있는 정수의 보안 문제 :

  1. 함수에 양의 매개 변수가 필요한 경우 하위 범위 확인을 쉽게 잊을 수 있습니다.
  2. 음의 정수 크기 변환으로 인한 직관적이지 않은 비트 패턴.
  3. 음의 정수의 오른쪽 시프트 연산으로 생성 된 직관적이지 않은 비트 패턴.

부호있는 <-> 부호없는 변환에 문제가 있으므로 믹스를 사용하지 않는 것이 좋습니다.


1
왜 좋은 대답입니까? 레시피 3.5 란 무엇입니까? 정수 오버플로 등에 대해 무엇을 말합니까?
Baldrickk

내 실제 경험에서 그것은 내가 시도한 다른 모든 측면에서 귀중한 조언을 제공하는 매우 좋은 책 이며이 권장 사항에서 꽤 견고합니다. 4G보다 긴 어레이에서 정수 오버플로의 위험과 비교하면 꽤 약한 것 같습니다. 큰 배열을 처리 해야하는 경우 내 프로그램은 성능 저하를 피하기 위해 많은 미세 조정을 수행합니다.
zzz777

1
책이 좋은지에 관한 것이 아닙니다. 귀하의 답변은 레시피 사용에 대한 타당성을 제공하지 않으며, 모든 사람이 책을 찾아 볼 수있는 것은 아닙니다. 좋은 답변을 쓰는 ​​방법의 예를보십시오
Baldrickk

참고로 다만 부호없는 정수를 사용하는 또 다른 이유에 대해 배웠습니다 : 하나는 쉽게 overlow 감지 할 수 있습니다 : youtube.com/...
zzz777
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.