부호 별 또는 크기별 유형과 비교하여 "int"를 사용해야하는 경우는 언제입니까?
나는이 프로그래밍 언어에 대한 약간의 VM을 그것은 32 비트 및 64 비트 아키텍처뿐만 아니라 모두 C 및 C ++에서 컴파일을 지원하고 C로 구현했습니다.
가능한 한 많은 경고를 활성화하여 깔끔하게 컴파일되도록 노력하고 있습니다. 을 켜면 CLANG_WARN_IMPLICIT_SIGN_CONVERSION
새로운 경고가 연속적으로 표시됩니다.
int
명시 적으로 서명되지 않은 유형 및 / 또는 명시 적으로 크기 가 지정된 유형과 비교하여 언제 사용할 지에 대한 좋은 전략을 갖고 싶습니다 . 지금까지 그 전략이 무엇인지 결정하는 데 어려움을 겪고 있습니다.
대부분 int
지역 변수 및 매개 변수와 같은 항목에 사용하고 구조체의 필드에 더 좁은 유형을 사용하여 이들을 혼합 하면 많은 암시 적 변환 문제가 발생한다는 것은 사실입니다.
힙의 개체에 대한 메모리 사용을 명시 적으로 제어하는 아이디어가 마음에 들기 때문에 구조체 필드에 대해보다 구체적으로 크기가 지정된 유형을 사용하는 것을 좋아합니다. 또한 해시 테이블의 경우 해시 할 때 서명되지 않은 오버플로에 의존하므로 해시 테이블의 크기가 uint32_t
.
하지만 어디에서나 더 구체적인 유형을 사용하려고하면 모든 곳 에서 구불 구불 한 캐스트의 미로에 빠져 있습니다.
다른 C 프로젝트는 무엇을합니까?
int
캐스팅의 필요성을 최소화하기 때문에 어디에서나 사용하는 것만으로도 유혹적인 것처럼 보일 수 있지만주의해야 할 몇 가지 잠재적 인 함정이 있습니다.
An
int
은 예상보다 짧을 수 있습니다. 대부분의 데스크탑 플랫폼에서 anint
은 일반적으로 32 비트이지만 C 표준은 최소 16 비트 길이 만 보장합니다 . 코드 에 임시 값인 경우에도 2 16 -1 = 32,767 보다 큰 숫자가 필요할 수 있습니까? 그렇다면int
. (long
대신 a를 사용할 수 있습니다 . along
는 32 비트 이상이어야합니다.)a조차도
long
항상 충분히 길지는 않습니다. 특히 배열 (또는 배열 인 문자열)의 길이char
가long
. 이를 위해size_t
(또는ptrdiff_t
부호 차이가 필요한 경우)를 사용하십시오 .특히 a
size_t
는 유효한 배열 인덱스를 보유 할 수있을만큼 충분히 크도록 정의되는 반면 anint
또는 along
는 그렇지 않을 수 있습니다. 따라서 예를 들어 배열을 반복 할 때 루프 카운터 (및 초기 / 최종 값)는 일반적으로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
오버플로가 정의되지 않은 동작 임을 의미합니다 . 가치가 합법적으로 넘칠 수있는 위험이 있다면int
; 를 사용unsigned int
(또는unsigned long
, 또는 ) 대신.uintNN_t
때로는 고정 된 비트 길이 만 필요합니다. ABI와 인터페이스하거나 특정 길이의 정수가 필요한 파일 형식을 읽고 / 쓰기하는 경우 사용해야하는 길이입니다. (물론, 그런 상황이라면 엔디안과 같은 것에 대해 걱정해야 할 수도 있고, 어쨌든 데이터를 바이트 단위로 수동 패킹해야 할 수도 있습니다.)
즉, 고정 길이 유형 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
이 유형은 크기가 충분히 크면 유형의 정확한 크기가 중요하지 않은 모든 곳에 사용합니다. 내가 제안한 유형 이름은 짧고 자체 문서화되어 있으므로 필요한 경우 캐스트에서 사용하기 쉽고 너무 좁은 유형 사용으로 인한 오류 위험을 최소화해야합니다.
편리하게도 위에서 정의한
u32
및u16
유형은 최소. 이상으로 보장unsigned int
되므로 승격되어 정의되지 않은 오버플로 동작을 유발할 염려없이 안전하게 사용할 수 있습니다 .int
사용하여
size_t
모든 배열의 크기와 인덱싱하지만, 및 기타 정수 유형 사이의 캐스팅 할 때주의해야합니다. 선택적으로 너무 많은 밑줄을 입력하고 싶지 않은 경우typedef
더 편리한 별칭도 사용할 수 있습니다.특정 비트 수에서 오버플로를 가정하는 계산의 경우를 사용 하거나 위에 정의 된대로 / 를 사용 하고 . 사용하기로 선택한 경우 예기치 않은 승진으로부터 자신을 보호하십시오 . 이를 수행하는 한 가지 방법은 다음과 같은 매크로를 사용하는 것입니다.
uintNN_t
u16
u32
&
uintNN_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 */
다시 말하지만,이 유형 이름은 크기와 목적에 대해 자체 문서화됩니다.
If the ABI might actually require, say, 16- or 64-bit ints instead, depending on the platform and/or compile-time options, you can change the type definition to match (and rename the type to just
fooint
) — but then you really do need to be careful whenever you cast anything to or from that type, because it might overflow unexpectedly.If your code has its own structures or file formats that require specific bitlengths, consider defining custom types for those too, exactly as if it was an external ABI. Or you could just use
uintNN_t
instead, but you'll lose a little bit of self-documentation that way.For all these types, don't forget to also define the corresponding
_MIN
and_MAX
constants for easy bounds checking. This might sound like a lot of work, but it's really just a couple of lines in a single header file.
Finally, remember to be careful with integer math, especially overflows. For example, keep in mind that the difference of two n-bit signed integers may not fit in an n-bit int. (It will fit into an n-bit unsigned int, if you know it's non-negative; but remember that you need to cast the inputs to an unsigned type before taking their difference to avoid undefined behavior!) Similarly, to find the average of two integers (e.g. for a binary search), don't use avg = (lo + hi) / 2
, but rather e.g. avg = lo + (hi + 0U - lo) / 2
; the former will break if the sum overflows.
You seem to know what you are doing, judging from the linked source code, which I took a glance at.
You said it yourself - using "specific" types makes you have more casts. That's not an optimal route to take anyway. Use int
as much as you can, for things that do not mandate a more specialized type.
The beauty of int
is that it is abstracted over the types you speak of. It is optimal in all cases where you need not expose the construct to a system unaware of int
. It is your own tool for abstracting the platform for your program(s). It may also yield you speed, size and alignment advantage, depending.
In all other cases, e.g. where you want to deliberately stay close to machine specifications, int
can and sometimes should be abandoned. Typical cases include network protocols where the data goes on the wire, and interoperability facilities - bridges of sorts between C and other languages, kernel assembly routines accessing C structures. But don't forget that sometimes you would want to in fact use int
even in these cases, as it follows platforms own "native" or preferred word size, and you might want to rely on that very property.
With platform types like uint32_t
, a kernel might want to use these (although it may not have to) in its data structures if these are accessed from both C and assembler, as the latter doesn't typically know what int
is supposed to be.
To sum up, use int
as much as possible and resort to moving from more abstract types to "machine" types (bytes/octets, words, etc) in any situation which may require so.
As to size_t
and other "usage-suggestive" types - as long as syntax follows semantics inherent to the type - say, using size_t
for well, size values of all kinds - I would not contest. But I would not liberally apply it to anything just because it is guaranteed to be the largest type (regardless if it is actually true). That's an underwater stone you don't want to be stepping on later. Code has to be self-explanatory to the degree possible, I would say - having a size_t
where none is naturally expected, would raise eyebrows, for a good reason. Use size_t
for sizes. Use offset_t
for offsets. Use [u]intN_t
for octets, words, and such things. And so on.
This is about applying semantics inherent in a particular C type, to your source code, and about the implications on the running program.
Also, as others have illustrated, don't shy away from typedef
, as it gives you the power to efficiently define your own types, an abstraction facility I personally value. A good program source code may not even expose a single int
, nevertheless relying on int
aliased behind a multitude of purpose-defined types. I am not going to cover typedef
here, the other answers hopefully will.
Keep large numbers that are used to access members of arrays, or control buffers as size_t
.
For an example of a project that makes use of size_t
, refer to GNU's dd.c, line 155.
Here are a few things I do. Not sure they're for everyone but they work for me.
- Never use
int
orunsigned int
directly. There always seems to be a more appropriately named type for the job. - If a variable needs to be a specific width (e.g. for a hardware register or to match a protocol) use a width-specific type (e.g.
uint32_t
). - For array iterators, where I want to access array elements 0 thru n, this should also be unsigned (no reason to access any index less than 0) and I use one of the fast types (e.g.
uint_fast16_t
), selecting the type based on the minimum size required to access all array elements. For example, if I have afor
loop that will iterate through 24 elements max, I'll useuint_fast8_t
and let the compiler (or stdint.h, depending how pedantic we want to get) decide which is the fastest type for that operation. - Always use unsigned variables unless there is a specific reason for them to be signed.
- If your unsigned variables and signed variables need to play together, use explicit casts and be aware of the consequences. (Luckily this will be minimized if you avoid using signed variables except where absolutely necessary.)
If you disagree with any of those or have recommended alternatives please let me know in the comments! That's the life of a software developer... we keep learning or we become irrelevant.
Always.
Unless you have specific reasons for using a more specific type, including you're on a 16-bit platform and need integers greater than 32767, or you need to ensure proper byte order and signage for data exchange over a network or in a file (and unless you're resource constrained, consider transferring data in "plain text," meaning ASCII or UTF8 if you prefer).
My experience has shown that "just use 'int'" is a good maxim to live by and makes it possible to turn out working, easily maintained, correct code quickly every time. But your specific situation may differ, so take this advice with a bit of well-deserved scrutiny.
ReferenceURL : https://stackoverflow.com/questions/29197964/when-should-i-just-use-int-versus-more-sign-specific-or-size-specific-types
'Development Tip' 카테고리의 다른 글
R의 가상 환경? (0) | 2021.01.08 |
---|---|
React vs Angular : React를 사용한 느린 렌더링 (0) | 2021.01.08 |
초기화되지 않은 변수를 인쇄하려고 시도해도 항상 오류 메시지가 표시되지 않는 이유 (0) | 2021.01.08 |
공식 C 문서와 같은 것이 있습니까? (0) | 2021.01.08 |
contentEditable DIV에서 줄 바꿈 처리 (0) | 2021.01.08 |