Program Tip

서명되지 않은 C ++보다 서명 된 것을 선호하는 이유는 무엇입니까?

programtip 2020. 11. 7. 10:27
반응형

서명되지 않은 C ++보다 서명 된 것을 선호하는 이유는 무엇입니까?


내가 더 잘 이해 이유를 선택하고 싶습니다 int이상 unsigned?

개인적으로 유효한 이유가없는 한 서명 된 값을 좋아하지 않았습니다. 예를 들어 배열의 항목 수, 문자열의 길이, 메모리 블록의 크기 등. 이러한 것들은 종종 음수가 될 수 없습니다. 이러한 값에는 가능한 의미가 없습니다. int그러한 모든 경우에 오해의 소지가있을 때 선호하는 이유는 무엇 입니까?

비얀 스트로브 스트 룹 챈들러 Carruth 모두가 선호하는 조언을 주었기 때문에 나는이 물어 int이상 unsigned (약 12시 반 ') 여기 .

intover short또는 long- 사용에 대한 주장 int이 대상 머신 아키텍처에 대한 "가장 자연스러운"데이터 폭이라는 ​​것을 알 수 있습니다.

그러나 서명되지 않은 서명은 항상 나를 괴롭 혔습니다. 서명 된 값이 일반적인 최신 CPU 아키텍처에서 진정으로 더 빠릅니까? 무엇이 그들을 더 좋게 만드는가?


전문가들이 간결하게 말한대로 비디오를 의역 해 보겠습니다.

Andrei Alexandrescu :

  • 간단한 지침이 없습니다.
  • 시스템 프로그래밍에서는 크기와 부호가 다른 정수가 필요합니다.
  • 많은 변환과 난해한 규칙이 (for와 같은 auto) 산술을 제어 하므로주의해야합니다.

Chandler Carruth :

  • 다음은 몇 가지 간단한 지침입니다.
    1. 2의 보수 산술이나 비트 패턴이 필요하지 않으면 부호있는 정수를 사용하십시오.
    2. 충분할 가장 작은 정수를 사용하십시오.
    3. 그렇지 않으면 int항목을 셀 수 있다고 생각 되면 사용 하고 , 셀 수있는 것보다 더 많은 경우 64 비트 정수를 사용하십시오.
  • 걱정하지 말고 도구를 사용하여 다른 유형이나 크기가 필요할 때 알려줍니다.

Bjarne Stroustrup :

  • int그럴 필요가 없을 때까지 사용하십시오 .
  • 비트 패턴에만 unsigned를 사용하십시오.
  • 서명 된 것과 서명되지 않은 것을 혼합하지 마십시오.

서명 규칙에 대한 경계는 제쳐두고, 내 한 문장은 전문가로부터 멀어집니다.

적절한 유형을 사용하고 모를 때는 알 int때까지 사용하십시오 .


의견의 요청에 따라 : int대신 선호 합니다 unsigned...

  1. 더 짧아요 (진지 해요!)

  2. 더 일반적이고 더 직관적입니다 (즉 1 - 2, 모호한 거대한 숫자가 아니라 -1 이라고 가정 할 수있는 것을 좋아합니다 )

  3. 범위를 벗어난 값을 반환하여 오류를 알리려면 어떻게해야합니까?

물론 반론이 있지만 이것이 내가 int대신 정수를 선언하는 것을 좋아하는 주된 이유 입니다 unsigned. 물론 이것이 항상 사실은 아닙니다. 다른 경우에는 an unsigned이 작업을위한 더 나은 도구 일뿐입니다. 저는 "왜 서명을 기본으로하는 것을 선호 하는가"라는 질문에 구체적으로 대답하고 있습니다.


몇 가지 이유:

  1. 산술은 unsigned항상 unsigned를 산출합니다. 이는 합리적으로 음의 결과를 초래할 수있는 정수량을 뺄 때 문제가 될 수 있습니다. 균형을 얻기 위해 화폐량을 빼거나 요소 사이의 거리를 산출하기 위해 인덱스를 배열하는 것을 생각해보십시오. 피연산자가 부호가 없으면 완벽하게 정의되지만 거의 의미가없는 결과를 얻고 result < 0비교는 항상 거짓이됩니다 (현대 컴파일러가 다행스럽게도 경고합니다).

  2. unsigned부호있는 정수와 혼합되는 산술을 오염시키는 불쾌한 속성이 있습니다. 따라서 부호있는 정수와 부호없는 값을 추가하고 결과가 0보다 큰지 묻는다면, 특히 부호없는 정수 유형이 뒤에 숨겨져있을 때 물릴 수 있습니다 typedef.


선호 할 이유가없는 signed이상 unsigned순수하게 사회 학적 사람을 제외하고는, 즉 어떤 사람들은 평균 프로그래머의 관점에서 적절한 코드를 작성하는 능력 및 / 또는 세심한 충분하지 않습니다 믿습니다 unsigned유형. 이것은 화자가 얼마나 존경을 받았는지에 관계없이 다양한 "화자"가 사용하는 주된 추론입니다.

실제로 유능한 프로그래머는 서명되지 않은 정수 유형 측면에서 적절한 코드를 작성할 수 있도록하는 기본 프로그래밍 관용구 및 기술을 빠르게 개발 및 / 또는 학습합니다.

또한 부호 산술 및 반복기 산술과 같은 C 및 C ++ 언어의 다른 부분에는 부호있는 의미 체계와 서명되지 않은 의미 체계 간의 근본적인 차이점이 항상 존재합니다 (표면적으로 다른 형식). 즉, 일반적으로 프로그래머는 서명되지 않은 의미론과 그에 따른 "문제"에 관련된 문제를 처리하는 것을 피할 수있는 옵션이 실제로 없습니다. 즉, 원하든 원하지 않든, unsigned정수 를 단호하게 피하더라도 왼쪽 끝에서 갑자기 끝나고 바로 여기에서 끝나는 범위 (먼 곳이 아님)로 작업하는 법을 배워야합니다 .

또한 아시다시피 표준 라이브러리의 많은 부분이 이미 unsigned정수 유형에 상당히 많이 의존하고 있습니다. 서명되지 않은 산술로 작업하는 것을 배우는 대신에 서명 된 산술을 혼합에 적용하면 비참하게 나쁜 코드 만 발생합니다.

떠오르는 일부 컨텍스트에서 선호하는 유일한 실제 이유 signed는 혼합 정수 / 부동 소수점 코드에서 signed정수 형식은 일반적으로 FPU 명령어 세트에서 직접 지원되는 반면 unsigned형식은 전혀 지원되지 않으므로 컴파일러가 다음에 대한 추가 코드를 생성하도록하기 때문입니다. 부동 소수점 값과 unsigned사이의 변환 . 이러한 코드 signed유형에서 더 나은 성능을 발휘할 수 있습니다.

그러나 동시에 순수하게 정수 코드 unsigned유형에서 유형보다 성능이 더 좋을 수 signed있습니다. 예를 들어, 정수 나누기는 언어 사양의 요구 사항을 충족하기 위해 추가 수정 코드가 필요한 경우가 많습니다. 정정은 음수 피연산자의 경우에만 필요하므로 음수 피연산자가 실제로 사용되지 않는 상황에서 CPU주기를 낭비합니다.

내 연습에서 나는 unsigned할 수있는 곳이면 어디든 헌신적으로 고수하고 signed필요한 경우에만 사용 합니다.


The integral types in C and many languages which derive from it have two general usage cases: to represent numbers, or represent members of an abstract algebraic ring. For those unfamiliar with abstract algebra, the primary notion behind a ring is that adding, subtracting, or multiplying two items of a ring should yield another item of that ring--it shouldn't crash or yield a value outside the ring. On a 32-bit machine, adding unsigned 0x12345678 to unsigned 0xFFFFFFFF doesn't "overflow"--it simply yields the result 0x12345677 which is defined for the ring of integers congruent mod 2^32 (because the arithmetic result of adding 0x12345678 to 0xFFFFFFFF, i.e. 0x112345677, is congruent to 0x12345677 mod 2^32).

개념적으로 두 가지 목적 (숫자를 나타내거나 mod 2 ^ n의 정수 링 멤버를 나타냄)은 부호있는 유형과 부호없는 유형 모두에 의해 제공 될 수 있으며 많은 작업이 두 사용 사례에서 동일하지만 약간의 차이가 있습니다. 무엇보다도 두 숫자를 더하려는 시도는 정확한 산술 합계 이외의 다른 결과를 산출하지 않아야합니다. 언어가 코드를 생성하지 않도록 보장하는 데 필요한 코드를 생성해야하는지 여부는 논란의 여지가 있지만 (예 : 예외가 대신 throw 될 것임) 정수 유형을 사용하여 숫자를 나타내는 코드의 경우 이러한 동작이 더 바람직 하다고 주장 할 수 있습니다. 산술적으로 잘못된 값을 산출하고 컴파일러가 그런 방식으로 행동하는 것을 금지해서는 안됩니다.

C 표준의 구현자는 숫자를 나타 내기 위해 부호있는 정수 유형을 사용하고 mod 2 ^ n 합동 정수의 대수 링 구성원을 나타 내기 위해 부호없는 유형을 사용하기로 결정했습니다. 대조적으로 Java는 부호있는 정수를 사용하여 이러한 링의 구성원을 나타내며 (일부 컨텍스트에서는 다르게 해석됩니다. 예를 들어 다른 크기의 부호있는 유형 간의 변환은 부호없는 유형간에 다르게 작동 함) Java에는 부호없는 정수도없고 예외적이지 않은 모든 경우에서 숫자로 작동하는 기본 정수 유형.

언어에서 숫자와 대수 링 숫자 모두에 대해 부호있는 표현과 부호없는 표현을 선택할 수 있다면 부호없는 숫자를 사용하여 항상 양수인 수량을 나타내는 것이 좋습니다. 그러나 부호없는 유형이 대수적 링의 구성원을 나타내고 숫자를 나타내는 유일한 유형이 부호있는 유형이라면 값이 항상 양수 일지라도 숫자를 나타내도록 설계된 유형을 사용하여 표현해야합니다.

덧붙여서, (uint32_t) -1이 0xFFFFFFFF 인 이유는 부호있는 값을 부호없는 값으로 캐스팅하는 것은 부호없는 0을 더하는 것과 같고 부호없는 값에 정수를 더하는 것은 그 크기를 더하거나 빼는 것으로 정의되기 때문입니다. X = YZ이면 X는 X + Z = Y와 같은 링의 유일한 구성원임을 지정하는 대수 링의 규칙에 따라 부호없는 값입니다. 부호없는 수학에서 0xFFFFFFFF는 부호없는 1에 더할 때 부호없는 0을 생성하는 유일한 숫자입니다.


속도는 현대 아키텍처에서도 동일합니다. 문제 unsigned int는 때때로 예기치 않은 동작을 생성 할 수 있다는 것입니다. 그렇지 않으면 나타나지 않을 버그를 만들 수 있습니다.

일반적으로 값에서 1을 빼면 값이 작아집니다. 이제 signedunsigned int변수 모두에서 1을 빼면 훨씬 더 큰 값이 생성되는 시간이 있습니다. 주요 차이점 unsigned intint하다와 unsigned int수를 서명으로 멀리 정상 동작에서 안전하게 반면 역설적 결과를 생성하는 값은 일반적으로 사용 --- --- 0 값이다.

오류 값에 대해 -1을 반환하는 한 --- 현대적인 생각은 반환 값을 테스트하는 것보다 예외를 던지는 것이 낫다는 것입니다.

코드를 적절하게 방어한다면이 문제가 발생하지 않을 것이고, 모든 곳에서 서명되지 않은 것을 종교적으로 사용한다면 괜찮을 것입니다 (단, 더하기 만하고 빼지 않고 MAX_INT에 가까워지지 않는 경우). 나는 모든 곳에서 unsigned int를 사용합니다. 하지만 많은 훈련이 필요합니다. 많은 프로그램의 경우 int다른 버그 를 사용 하여 시간을 보낼 수 있습니다.


실제 질문에 답하기 위해 : 방대한 양의 경우에는 그다지 중요하지 않습니다. int첫 번째 피연산자보다 큰 두 번째 피연산자를 사용하여 빼기와 같은 작업을 처리하는 것이 조금 더 쉬울 수 있으며 여전히 "예상 된"결과를 얻을 수 있습니다.

부호있는 숫자와 부호없는 숫자에 대해 다른 명령어 만 다음과 같기 때문에 99.9 %의 경우 속도 차이가 전혀 없습니다.

  1. Making the number longer (fill with the sign for signed or zero for unsigned) - it takes the same effort to do both.
  2. Comparisons - a signed number, the processor has to take into account if either number is negative or not. But again, it's the same speed to make a compare with signed or unsigned numbers - it's just using a different instruction code to say "numbers that have the highest bit set are smaller than numbers with the highest bit not set" (essentially). [Pedantically, it's nearly always the operation using the RESULT of a comparison that is different - the most common case being a conditional jump or branch instruction - but either way, it's the same effort, just that the inputs are taken to mean slightly different things].
  3. Multiply and divide. Obviously, sign conversion of the result needs to happen if it's a signed multiplication, where a unsigned should not change the sign of the result if the highest bit of one of the inputs is set. And again, the effort is (as near as we care for) identical.

(I think there are one or two other cases, but the result is the same - it really doesn't matter if it's signed or unsigned, the effort to perform the operation is the same for both).


  1. Use int by default: it plays nicer with the rest of the language

    • most common domain usage is regular arithmetic, not modular arithmetic
    • int main() {} // see an unsigned?
    • auto i = 0; // i is of type int
  2. Only use unsigned for modulo arithmetic and bit-twiddling (in particular shifting)

    • has different semantics than regular arithmetic, make sure it is what you want
    • bit-shifting signed types is subtle (see comments by @ChristianRau)
    • if you need a > 2Gb vector on a 32-bit machine, upgrade your OS / hardware
  3. Never mix signed and unsigned arithmetic

    • the rules for that are complicated and surprising (either one can be converted to the other, depending on the relative type sizes)
    • turn on -Wconversion -Wsign-conversion -Wsign-promo (gcc is better than Clang here)
    • the Standard Library got it wrong with std::size_t (quote from the GN13 video)
    • use range-for if you can,
    • for(auto i = 0; i < static_cast<int>(v.size()); ++i) if you must
  4. Don't use short or large types unless you actually need them

    • current architectures data flow caters well to 32-bit non-pointer data (but note the comment by @BenVoigt about cache effects for smaller types)
    • char and short save space but suffer from integral promotions
    • are you really going to count to over all int64_t?

The int type more closely resembles the behavior of mathematical integers than the unsigned type.

It is naive to prefer the unsigned type simply because a situation does not require negative values to be represented.

The problem is that the unsigned type has a discontinuous behavior right next to zero. Any operation that tries to compute a small negative value, instead produces some large positive value. (Worse: one that is implementation-defined.)

Algebraic relationships such as that a < b implies that a - b < 0 are wrecked in the unsigned domain, even for small values like a = 3 and b = 4.

A descending loop like for (i = max - 1; i >= 0; i--) fails to terminate if i is made unsigned.

Unsigned quirks can cause a problem which will affect code regardless of whether that code expects to be representing only positive quantities.

The virtue of the unsigned types is that certain operations that are not portably defined at the bit level for the signed types are that way for the unsigned types. The unsigned types lack a sign bit, and so shifting and masking through the sign bit isn't a problem. The unsigned types are good for bitmasks, and for code that implements precise arithmetic in a platform-independent way. Unsigned opearations will simulate two's complement semantics even on a non two's complement machine. Writing a multi-precision (bignum) library practically requires arrays of unsigned types to be used for the representation, rather than signed types.

The unsigned types are also suitable in situations in which numbers behave like identifiers and not as arithmetic types. For instance, an IPv4 address can be represented in a 32 bit unsigned type. You wouldn't add together IPv4 addresses.


int is preferred because it's most commonly used. unsigned is usually associated with bit operations. Whenever I see an unsigned, I assume it's used for bit twiddling.

If you need a bigger range, use a 64-bit integer.

If you're iterating over stuff using indexes, types usually have size_type, and you shouldn't care whether it's signed or unsigned.

Speed is not an issue.


For me, in addition to all the integers in the range of 0..+2,147,483,647 contained within the set of signed and unsigned integers on 32 bit architectures, there is a higher probability that I will need to use -1 (or smaller) than need to use +2,147,483,648 (or larger).


One good reason that I can think of is in case of detecting overflow.

For the use cases such as the count of items in an array, length of a string, or size of memory block, you can overflow an unsigned int and you may not notice a difference even when you take a look at the variable. If it is an signed int, the variable will be less than zero and clearly wrong.

You can simply check to see if the variable is zero when you want to use it. This way, you do not have to check for overflow after every arithmetic operation as is the case for unsigned ints.


It gives unexpected result when doing simple arithmetic operation:

unsigned int i;
i = 1 - 2;
//i is now 4294967295 on a 64bit machine

It gives unexpected result when doing simple comparison:

unsigned int j = 1;
std::cout << (j>-1) << std::endl;
//output 0 as false but 1 is greater than -1

This is because when doing the operations above, the signed ints are converted to unsigned, and it overflows and goes to a really big number.

참고URL : https://stackoverflow.com/questions/18795453/why-prefer-signed-over-unsigned-in-c

반응형