타입 - vector unsigned int




"int"와 더 많은 부호 별 또는 크기별 유형을 언제 사용해야합니까? (4)

링크 소스 코드로 판단하여 내가 한 일을 알고있는 것 같습니다.

"특정"유형을 사용하면 더 많은 캐스팅을 할 수 있습니다. 어쨌든 최적의 경로는 아닙니다. 더 특수한 유형을 요구하지 않는 경우 가능한 한 int 를 사용하십시오.

int 장점은 당신이 말하는 유형에 따라 추상화된다는 것입니다. int 모르는 시스템에 구성을 노출시킬 필요가없는 모든 경우에 최적입니다. 프로그램 플랫폼을 추상화하기위한 도구입니다. 또한 속도, 크기 및 정렬 이점을 얻을 수 있습니다.

예를 들어 의도적으로 기계 사양에 가깝게 유지하려는 다른 모든 경우에는 int 를 포기하고 때로는 포기해야합니다. 일반적인 사례로는 데이터가 유선으로 연결되는 네트워크 프로토콜 및 C와 다른 언어 간의 브리지, C 구조에 액세스하는 커널 어셈블리 루틴 등의 상호 운용성 기능이 있습니다. 그러나 플랫폼 자체의 "네이티브"또는 선호하는 단어 크기를 따르기 때문에 이러한 경우에도 실제로 int 사용하고 싶을 때 해당 속성에 의존하고 싶을 수도 있습니다.

uint32_t 와 같은 플랫폼 유형을 사용하면 커널은 C와 어셈블러 모두에서 액세스하는 경우 데이터 구조에서 이러한 구조를 사용하려고 할 수 있습니다 ( uint32_t 는 아님). 후자는 일반적으로 int 가 무엇인지 알지 못하기 때문에

요약하자면, 가능한 한 int 를 사용하고 필요할 수있는 상황에서 더 추상적 인 유형에서 "machine"유형 (바이트 / 옥텟, 단어 등)으로 이동하십시오.

size_t 와 다른 "usage-suggestive"타입에 관해서는-구문이 타입 고유의 의미론을 따르는 한-예를 들어 size_t 를 사용하여 모든 종류의 크기 값 -경쟁하지 않습니다. 그러나 나는 그것이 실제로 가장 큰 유형이라는 것이 보장되기 때문에 아무것도 자유롭게 적용하지 않을 것입니다. 그것은 나중에 밟고 싶지 않은 수중 석재입니다. 코드는 가능한 한 자명해야합니다. 자연스럽게 예상되는 곳이없는 size_t 가 있으면 눈썹을 올릴 수 있습니다. 크기에는 size_t 를 사용하십시오. 오프셋에는 offset_t 를 사용하십시오. 옥텟, 단어 [u]intN_t[u]intN_t 를 사용하십시오. 등등.

특정 C 유형 고유의 의미론을 소스 코드에 적용하고 실행중인 프로그램에 미치는 영향에 관한 것입니다.

또한 다른 사람들이 설명 typedef 에서 벗어나지 마십시오. 유형을 효율적으로 정의 할 수있는 힘을 제공하므로 개인적으로 중요하게 생각하는 추상화 기능입니다. 좋은 프로그램 소스 코드는 단일 int 노출하지 않을 수도 있지만, 여러 가지 목적으로 정의 된 유형 뒤에 별칭이 지정된 int 의존합니다. 나는 여기서 typedef 를 다루지 않을 것이고, 다른 대답은 희망적으로 될 것입니다.

C 구현 된 프로그래밍 언어를위한 작은 VM이 있습니다 . 32 비트 및 64 비트 아키텍처와 C 및 C ++ 모두에서 컴파일되는 것을 지원합니다.

가능한 많은 경고를 사용하여 깨끗하게 컴파일하려고합니다. CLANG_WARN_IMPLICIT_SIGN_CONVERSION 켜면 일련의 새로운 경고가 나타납니다.

int 대 명시 적으로 부호없는 유형 및 / 또는 명시 적으로 크기가 지정된 유형을 사용할 때에 대한 좋은 전략을 갖고 싶습니다. 지금까지 그 전략을 결정하는 데 어려움을 겪고 있습니다.

지역 변수 및 매개 변수와 같은 것들에 주로 int 를 사용하고 구조체의 필드에 더 좁은 유형을 사용하여 그것들을 혼합하면 많은 암시 적 변환 문제가 발생한다는 것은 사실입니다.

힙의 객체에 대한 메모리 사용을 명시 적으로 제어한다는 아이디어가 좋아서 구조체 필드에 더 구체적으로 크기가 지정된 유형을 사용하는 것을 좋아합니다. 또한 해시 테이블의 경우 해싱 할 때 부호없는 오버플로에 의존하므로 해시 테이블의 크기가 uint32_t 로 저장되어 있으면 좋습니다.

그러나 어디에서나 더 구체적인 유형을 사용하려고하면 어디에서나 뒤틀린 캐스트의 미로에 빠져 있습니다.

다른 C 프로젝트는 무엇을합니까?


배열 구성원에 액세스하거나 버퍼를 size_t 로 제어하는 ​​데 사용되는 많은 수를 유지하십시오.

size_t 를 사용하는 프로젝트의 예는 GNU dd.c, line 155를 참조하십시오.


캐스팅의 필요성을 최소화하기 때문에 어디에서나 int 사용하는 것은 유혹적 인 것처럼 보일 수 있지만 알아야 할 몇 가지 함정이 있습니다.

  • int 는 예상보다 짧을 수 있습니다. 대부분의 데스크탑 플랫폼에서 int 는 일반적으로 32 비트이지만 C 표준은 최소 16 비트 길이 만 보장합니다 . 코드에 임시 값이 있더라도 2 16 −1 = 32,767보다 큰 숫자가 필요할 수 있습니까? 그렇다면 int 사용하지 마십시오. (대신 long 을 사용하고 싶을 수 있습니다. long 은 32 비트 이상이어야합니다.)

  • long 것조차도 항상 충분하지는 않습니다. 특히, 배열 (또는 char 배열 인 문자열)의 longlong 맞는다는 보장은 없습니다. 그것들에 size_t (또는 부호있는 차이가 필요한 경우 ptrdiff_t 사용하십시오.

    특히, size_t 는 유효한 배열 인덱스를 보유하기에 충분히 크도록 정의 되지만 int 또는 long 은 그렇지 않을 수 있습니다. 따라서 예를 들어 배열을 반복 할 때 배열이 더 작은 유형이 작동하기에 충분히 짧은 지 확실하지 않으면 루프 카운터 (및 초기 / 최종 값)는 일반적으로 size_t 가되어야합니다. (그러나 거꾸로 반복 할 때 조심하십시오 : size_t 는 부호가 없으므로 for(size_t i = n-1; i >= 0; i--) 는 무한 루프입니다! i != SIZE_MAX 또는 i != (size_t) -1 작동하지만 do / while 루프를 사용해야하지만 n == 0 !

  • int 가 서명되었습니다. 특히 이것은 int overflow가 정의되지 않은 동작 이라는 것을 의미합니다 . 값이 합법적으로 오버플로 될 위험이있는 경우에는 int 사용하지 마십시오. 대신 unsigned int (또는 unsigned long 또는 uint NN _t )를 사용하십시오.

  • 때로는 고정 비트 길이 만 있으면됩니다. ABI와 인터페이스하거나 파일 형식을 읽거나 쓰는 경우 특정 길이의 정수가 필요한 경우 사용하는 길이입니다. (물론 그러한 상황이므로 엔디안과 같은 것들에 대해 걱정해야 할 수도 있으므로 때로는 바이트 단위로 데이터를 수동으로 패킹해야 할 수도 있습니다.)

그러나 고정 길이 유형을 항상 사용하지 않는 이유도 있습니다. int32_tint32_t 입력하기가 int32_t 뿐만 아니라 컴파일러가 항상 32 비트 정수를 사용하도록 강요하는 것이 특히 최적은 아닙니다. 여기서 기본 int 크기는 64 비트 일 수 있습니다. 예를 들어 C99 int_fast32_t 사용할 수 있지만 입력하기가 더 어색합니다.

따라서 다음은 최대의 안전과 휴대 성을위한 개인적인 제안입니다.

  • 다음과 같이 일반적인 헤더 파일에서 일상적으로 사용할 고유 한 정수 유형을 정의하십시오 .

    #include <limits.h>
    typedef int i16;
    typedef unsigned int u16;
    #if UINT_MAX >= 4294967295U
      typedef int i32;
      typedef unsigned int u32;
    #else
      typedef long i32;
      typedef unsigned long i32;
    #endif

    크기가 충분히 크면 유형의 정확한 크기가 중요하지 않은 모든 항목에이 유형을 사용하십시오. 내가 제안한 유형 이름은 짧고 자체 문서화되므로 필요한 경우 캐스트에서 사용하기 쉽고 너무 좁은 유형을 사용하여 오류의 위험을 최소화해야합니다.

    편리하게, 위와 같이 정의 된 u32u16 유형은 최소한 unsigned int 만큼 넓게 보장되므로 int 승격되어 정의되지 않은 오버 플로우 동작을 유발할 염려없이 안전하게 사용할 수 있습니다 .

  • 모든 배열 크기 및 인덱싱에 size_t 를 사용하지만 다른 정수 유형간에 캐스팅 할 때는주의하십시오. 선택적으로 너무 많은 밑줄을 입력하지 않으려면 더 편리한 별칭을 typedef 로 입력하십시오.

  • 특정 비트 수에서 오버플로를 가정하는 계산의 경우 uint NN _t 를 사용 u32 정의 u16 / u32 를 사용하고 & 명시 적 비트 u16 사용 u16 . uint NN _t 를 사용하기로 선택한 경우 int 로의 예기치 않은 승격으로부터 자신을 보호하십시오. 이를 수행하는 한 가지 방법은 다음과 같은 매크로를 사용하는 것입니다.

    #define u(x) (0U + (x))

    다음과 같이 안전하게 작성해야합니다. 예 :

    uint32_t a = foo(), b = bar();
    uint32_t c = u(a) * u(b);  /* this is always unsigned multiply */
  • 특정 정수 길이가 필요한 외부 ABI의 경우 다음과 같이 특정 유형을 다시 정의하십시오.

    typedef int32_t fooint32;  /* foo ABI needs 32-bit ints */

    이 유형 이름은 크기와 목적에 관한 자체 문서화입니다.

    플랫폼 및 / 또는 컴파일 타임 옵션에 따라 ABI가 실제로 16 비트 또는 64 비트 ints를 요구할 수있는 경우, 유형 정의를 일치하도록 변경하고 유형을 fooint 변경하면됩니다. 예기치 않은 오버플로가 발생할 수 있으므로 해당 유형으로 또는 다른 유형으로 캐스트 할 때마다주의해야합니다.

  • 코드에 특정 비트 길이가 필요한 자체 구조 또는 파일 형식이있는 경우 외부 ABI 인 것처럼 해당 유형에 대한 사용자 정의 유형도 정의하는 것이 좋습니다. 또는 대신 uint NN _t 를 사용할 수도 있지만 그렇게하면 자체 문서화가 약간 손실됩니다.

  • 이러한 모든 유형에 대해 쉬운 경계 확인을 위해 해당 _MIN_MAX 상수를 정의하는 것을 잊지 마십시오. 이것은 많은 작업처럼 들릴지 모르지만 실제로는 단일 헤더 파일의 두 줄에 불과합니다.

마지막으로 정수 수학, 특히 오버플로에주의해야합니다. 예를 들어, 2 개의 n 비트 부호있는 정수의 차이가 n 비트 int에 맞지 않을 수 있습니다. (음수가 아닌 것을 알고 있다면 n 비트 부호없는 int에 맞을 것입니다. 그러나 정의되지 않은 동작을 피하기 위해 차이를 취하기 전에 입력을 부호없는 유형으로 캐스트해야한다는 것을 기억하십시오!) 2 개의 정수 (예 : 이진 검색의 경우)는 avg = (lo + hi) / 2 를 사용하지 않고 오히려 avg = lo + (hi + 0U - lo) / 2 . 합계가 넘치면 전자가 중단됩니다.


항상.

16 비트 플랫폼을 사용하고 32767보다 큰 정수가 필요하거나 네트워크 또는 파일을 통한 데이터 교환을위한 적절한 바이트 순서 및 신호를 보장해야하는 등 더 구체적인 유형을 사용해야하는 특별한 이유가없는 한 ( 리소스가 제한되지 않는 한 원하는 경우 ASCII 또는 UTF8을 의미하는 "일반 텍스트"로 데이터를 전송하는 것이 좋습니다.)

내 경험에 따르면 " 'int'만 사용하는 것 '은 살기 좋은 최대 값이며 매번 작업하고 쉽게 유지 관리하며 올바른 코드를 신속하게 만들 수 있습니다. 그러나 상황에 따라 상황이 다를 수 있으므로 충분히 신중하게 조사하여이 조언을 받으십시오.





signed