임베디드 코드의 경우 "unsigned int"대신 "uint_t"유형을 사용해야하는 이유는 무엇입니까?


22

gcc를 사용하여 STM32F105에 대한 응용 프로그램을 c로 작성하고 있습니다.

과거에는 (간단한 프로젝트로) 항상 변수를 char,, int등 으로 정의 unsigned int했습니다.

나는 같은 stdint.h에 정의 된 유형을 사용하는 것이 일반적입니다 것을 볼 int8_t, uint8_t, uint32_t, 등이 그것 여러 API의 내가 사용하고 있음을, 또한 ST의 ARM CMSIS 라이브러리에서 사실.

나는 왜 우리가 그렇게해야하는지 이해한다고 믿는다. 컴파일러가 메모리 공간을 더 잘 최적화 할 수 있습니다. 추가 이유가있을 것으로 예상됩니다.

그러나 c의 정수 승격 규칙으로 인해 두 개의 값을 추가하거나 비트 연산 등을 시도 할 때마다 변환 경고에 대해 계속 실행됩니다. 경고는 다음과 같습니다 conversion to 'uint16_t' from 'int' may alter its value [-Wconversion]. 이 문제는 여기여기에서 논의 됩니다 .

int또는로 선언 된 변수를 사용할 때는 발생하지 않습니다 unsigned int.

다음과 같은 몇 가지 예를 들자면 :

uint16_t value16;
uint8_t value8;

나는 이것을 바꿔야 할 것이다.

value16 <<= 8;
value8 += 2;

이에:

value16 = (uint16_t)(value16 << 8);
value8 = (uint8_t)(value8 + 2);

추악하지만 필요한 경우 할 수 있습니다. 내 질문은 다음과 같습니다.

  1. 에서 변환 경우가 부호 에가 서명 하고 다시 서명이 결과가 잘못된 것은?

  2. stdint.h 정수 유형을 사용하는 또 다른 큰 이유가 있습니까?

내가받는 답변을 바탕으로 stdint.h 유형이 일반적으로 선호되는 것처럼 보입니다 .c는 변환 하고 다시 변환 uint합니다 int. 이것은 더 큰 질문으로 이어집니다.

  1. typecasting (예 :)을 사용하여 컴파일러 경고를 방지 할 수 있습니다 value16 = (uint16_t)(value16 << 8);. 방금 문제를 숨기고 있습니까? 더 좋은 방법이 있습니까?

부호없는 리터럴 (예 : 8u및)을 사용하십시오 2u.
Harming Monica 중지

감사합니다, @OrangeDog, 오해라고 생각합니다. 나는 모두를 시도 value8 += 2u;하고 value8 = value8 + 2u;있지만이 같은 경고를 얻을.
bitsmack

이미 폭 경고 :이 없을 때 서명 경고를 피하기 위해, 어쨌든 사용
정지에 지장을주지 모니카

답변:


11

int17 비트에서 32 비트 사이 의 표준 준수 컴파일러 는 다음 코드를 사용하여 원하는 모든 작업을 합법적으로 수행 할 수 있습니다 .

uint16_t x = 46341;
uint32_t y = x*x; // temp result is signed int, which can't hold 2147488281

그렇게하려는 구현은 상상할 수있는 모든 프로토콜을 사용하여 모든 포트 핀에서 문자열 "Fred"를 반복적으로 출력하는 것 외에는 아무것도하지 않는 프로그램을 합법적으로 생성 할 수 있습니다. 그러한 일을하는 구현으로 프로그램이 이식 될 확률은 매우 낮지 만 이론적으로는 가능합니다. 정의되지 않은 동작에 관여하지 않도록 위의 코드를 작성하려면 후자의 식을 (uint32_t)x*x또는 로 작성해야합니다 1u*x*x. int17 비트와 31 비트 사이 의 컴파일러에서 후자의 표현식은 상위 비트에서 벗어나지 만 정의되지 않은 동작에는 관여하지 않습니다.

gcc 경고는 아마도 작성된 코드가 완전히 100 % 이식 가능하지 않다고 제안하고 있다고 생각합니다. 일부 구현에서는 정의되지 않은 동작을 피하기 위해 코드를 실제로 작성해야 할 때가 있지만, 다른 많은 경우에는 지나치게 성가신 작업에 코드가 사용되지 않을 것이라고 생각해야합니다.

같은 유형을 사용합니다 int및 것은 short몇 가지 경고를 제거하고 몇 가지 문제를 해결하지만, 가능성이 다른 사람을 만들 것입니다 수 있습니다. uint16_tC의 정수 승격 규칙과 같은 유형 간의 상호 작용 은 불안정하지만 이러한 유형은 여전히 ​​다른 대안보다 낫습니다.


6

1) 사이에 아무런 연산없이 동일한 길이의 부호없는 정수에서 부호없는 정수로 캐스트하면 매번 동일한 결과를 얻을 수 있으므로 여기서 아무런 문제가 없습니다. 그러나 다양한 논리 및 산술 연산은 부호있는 피연산자와 부호없는 피연산자에 대해 다르게 작동합니다.
2) 사용의 주요 이유 stdint.h유형은 이러한 유형의 비트 크기가 정의에 대한 사실이 아니다 플랫폼의 모든 걸쳐 동일이다 int, long등뿐만 아니라 char표준 signess, 그것은 서명하거나하여 서명 할 수있다 태만. 추가 검사 및 가정없이 정확한 크기를 알고있는 데이터를보다 쉽게 ​​조작 할 수 있습니다.


2
의 크기 int32_t와는 uint32_t모든 플랫폼에서 동일한 가 정의되는 . 프로세서에 정확히 일치하는 하드웨어 유형이 없으면 이러한 유형이 정의되지 않습니다. 따라서 int등 의 장점 , 아마도 int_least32_t
Pete Becker

1
@PeteBecker-컴파일 오류가 발생하면 즉시 문제를 알 수 있기 때문에 이점이 있습니다. 나는 내 유형이 나에게 크기를 변경하는 것보다 훨씬 더 싶습니다.
sapi

@sapi-많은 상황에서 기본 크기는 관련이 없습니다. C 프로그래머는 몇 년 동안 고정 된 크기없이 잘 작동했습니다.
Pete Becker

6

유진의 # 2가 아마도 가장 중요한 점이므로, 나는 이것이 권고라고 덧붙이고 싶습니다.

MISRA (directive 4.6): "typedefs that indicate size and signedness should be used in place of the basic types".

또한 Jack Ganssle은 그 규칙을지지하는 것으로 보입니다 : http://www.ganssle.com/tem/tem265.html


2
"N 비트 부호없는 정수를 안전하게 곱할 수있는 동일한 크기의 결과를 얻기 위해 다른 동일한 크기의 정수를 곱할 수 있음"을 지정하는 유형이없는 것은 너무 나쁩니다. 정수 프로모션 규칙은와 같은 기존 유형과 끔찍하게 상호 작용 uint32_t합니다.
supercat

3

경고를 제거하는 쉬운 방법은 GCC에서 -Wconversion을 사용하지 않는 것입니다. 이 옵션을 수동으로 활성화해야한다고 생각하지만 그렇지 않은 경우 -Wno-conversion을 사용하여 비활성화 할 수 있습니다. 원하는 경우 다른 옵션을 통해 부호 및 FP 정밀 변환에 대한 경고를 활성화 할 수 있습니다 .

-Wconversion 경고는 거의 항상 오탐 (false positive)이므로 -Wextra에서도 기본적으로이를 사용하지 않는 이유가 있습니다. 스택 오버플로 질문은 좋은 옵션 세트에 대한 제안을 많이했다. 내 자신의 경험을 바탕으로 시작하기에 좋은 곳입니다.

-std = c99 -pedantic-벽 -Wextra-그림자

필요한 경우 더 추가하십시오. 그러나 가능성은 없습니다.

-Wconversion을 유지해야하는 경우 숫자 피연산자 만 유형 변환하여 코드를 약간 줄일 수 있습니다.

value16 <<= (uint16_t)8;
value8 += (uint8_t)2;

그래도 구문 강조 표시 없이는 읽기가 쉽지 않습니다.


2

모든 소프트웨어 프로젝트에서 휴대용 유형 정의를 사용하는 것이 매우 중요합니다. (동일한 컴파일러의 다음 버전에서도 이러한 고려 사항이 필요합니다.) 좋은 예, 몇 년 전에 현재 컴파일러가 'int'를 8 비트로 정의한 프로젝트를 진행했습니다. 다음 버전의 컴파일러는 'int'를 16 비트로 정의했습니다. 우리는 'int'에 대해 이식 가능한 정의를 사용하지 않았기 때문에 램의 크기가 사실상 두 배가되었고 8 비트 int에 의존하는 많은 코드 시퀀스가 ​​실패했습니다. 이식 가능한 형식 정의를 사용하면 (수백 시간의 문제 해결) 문제를 피할 수있었습니다.


int8 비트 유형을 나타내는 데 합리적인 코드를 사용해서는 안됩니다 . CCS와 같은 비 C 컴파일러가 그렇게해도 합리적인 코드는 char8 비트에 대해 typedefed 유형을, 16 비트에 대해 typedefed 유형 ( "long"이 아님)을 사용해야합니다. 반면에, CCS와 같은 코드를 실제 컴파일러로 코드를 포팅하는 것은 적절한 typedef를 사용하더라도 문제가되기 쉽다. 왜냐하면 이러한 컴파일러는 다른 방식으로 종종 "비정상적"이기 때문이다.
supercat

1
  1. 예. n 비트 부호있는 정수는 음이 아닌 숫자의 수의 약 절반을 n 비트 부호없는 정수로 나타낼 수 있으며 오버플로 특성에 의존하는 것은 정의되지 않은 동작이므로 모든 일이 발생할 수 있습니다. 현재 및 과거 프로세서의 대다수는 2의 보수를 사용하므로 많은 연산이 부호있는 및 부호없는 정수 유형에 대해 동일한 작업을 수행하지만 모든 연산이 비트 단위로 동일한 결과를 생성하지는 않습니다. 코드가 의도 한대로 작동하지 않는 이유를 알 수없는 경우 나중에 추가 문제가 발생합니다.

  2. intunsigned 에는 구현에 정의 된 크기가 있지만 크기 나 속도 때문에 구현에 의해 "스마트하게"선택되는 경우가 많습니다. 나는 달리 다른 이유가없는 한 일반적으로 이것들을 고수합니다. 마찬가지로 int 또는 unsigned를 사용할지 고려할 때 달리 다른 이유가없는 한 일반적으로 int를 선호합니다.

유형의 크기 또는 부호를 더 잘 제어 해야하는 경우 일반적으로 시스템 정의 typedef (size_t, intmax_t 등)를 사용하거나 주어진 기능을 나타내는 나만의 typedef를 선호합니다 유형 (prng_int, adc_int 등).


0

종종 코드는 ARM 썸 및 AVR (및 x86, powerPC 및 기타 아키텍처) 에서 사용되며 8 비트에 맞는 변수 인 경우에도 STM32 ARM에서 16 비트 또는 32 비트가 더 효율적일 수 있습니다 (플래시 및 사이클) . 8 비트가 AVR에서 더 효율적입니다) . 그러나 SRAM이 거의 가득 찬 경우 전역 변수의 경우 8 비트로 되 돌리는 것이 합리적 일 수 있습니다 (그러나 로컬 변수는 아닙니다). 이식성 및 유지 관리 (특히 8 비트 변수의 경우) 의 경우 .h 장소 (일반적으로 ifdef)에서 정확한 크기 및 typedef 대신 MINIMUM 적합한 크기지정하는 것이 장점 (단점 없음)을 갖습니다 (아마도 uint_fast8_t / 포팅 / 빌드 시간 동안 uint_least8_t)

// apparently uint16_t is just as efficient as 32 bit on STM32, but 8 bit is punished (with more flash and cycles)
typedef uint16_t uintG8_t; // 8bit if SRAM is scarce (use fol global vars that fit in 8 bit)
typedef uint16_t uintL8_t; // 8bit on AVR (local var, 16 or 32 bit is more efficient on STM + less flash)
// might better reserve 32 bits on some arch, STM32 seems efficient with 16 bits:
typedef uint16_t uintG16_t; // 16bit if SRAM is scarce (use fol global vars that fit in 16 bit)
typedef uint16_t uintL16_t; // 16bit on AVR (local var, 16 or 32 bit whichever is more efficient on other arch)

GNU 라이브러리 는 약간 도움이되지만 일반적으로 typedef는 의미가 있습니다.

typedef uint_least8_t uintG8_t;
typedef uint_fast8_t uintL8_t;

// SRAM이 문제가되지 않을 때 BOTH는 uint_fast8_t입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.