41

I have been pondering on whether or not I should use the typedefs inside <cstdint> or not.

I personally prefer writing uint32_t over unsigned int and int8_t over char etc... since it to me is alot more intuitive.

What do you guys think? Is it a good idea to use the typedefs from <cstdint> or not? Are there any disadvantages?

Alex Bitek
  • 6,273
  • 4
  • 44
  • 77
ronag
  • 43,567
  • 23
  • 113
  • 204
  • 11
    These types aren't equivalent. –  May 26 '11 at 20:31
  • To me it is about a million times less intuitive. –  May 26 '11 at 20:32
  • 2
    Prefer to use POD types unless size of the variable is important to the application. Embedded systems are well known for using size-specific variables whereas desktop applications are less concerned. Database applications may require size specific variables. – Thomas Matthews May 26 '11 at 20:37
  • 3
    @Thomas: `uint32_t` and friends are POD; they are `typedef`'s inherited from C99. – Fred Foo May 26 '11 at 20:49
  • I'm terming POD as classical `int, unsigned int, char`, etc. – Thomas Matthews May 26 '11 at 21:14
  • 4
    @Thomas : POD already has a defined meaning; those are called 'primitive' or 'fundamental' types. – ildjarn May 26 '11 at 21:32
  • 1
    @ildjarn: I agree that POD is not the right term here. However, `uint32_t` and friends are also "primitive" types. We probably need a term like "classic" or "K&R" to describe the historically available types. – Ben Voigt May 27 '11 at 00:41
  • @BenVoigt : Right, I didn't mean to imply any distinction between e.g. `unsigned` and `uint32_t` in terms of classification; I only meant to criticize incorrect usage of 'POD'. – ildjarn May 27 '11 at 00:45
  • @BenVoigt : Oh, and what camp does `long long` fall into? ;-P – ildjarn May 27 '11 at 00:45
  • @ildjarn: "recently standardized gcc extension"? – Ben Voigt May 27 '11 at 02:08
  • @BenVoigt : Haha, that works. ;-] – ildjarn May 27 '11 at 02:21
  • @ildjarn: and then there's the recently standardized msvc extension, `nullptr_t` – Ben Voigt May 27 '11 at 02:38
  • @BenVoigt : I like how it's defined in VC++ 2010 -- `typedef decltype(nullptr) nullptr_t;`. – ildjarn May 27 '11 at 02:39

6 Answers6

37

Actually, I would suggest using both.

If you want something that is definitely 32-bits unsigned, use uint32_t. For example, if you are implementing a "struct" to represent an external object whose specification defines one of its fields as 32 bits unsigned.

If you want something that is the "natural word size of the machine", use int or unsigned int. For example:

for (int i = 0 ; i < 200 ; ++i)
    // stuff

The "natural word size of the machine" is going to give you the best performance, both on today's processors and on tomorrow's.

Use "char" if you mean "character"; "char" or "unsigned char" if you mean "byte". C/C++ lets you access an arbitrary object's bytes via "char *", not anything else, strictly speaking.

Use uint8_t or int8_t if you specifically want an 8-bit integer, similar to uint32_t.

Nemo
  • 65,634
  • 9
  • 110
  • 142
  • 2
    Wouldn't use size_t instead of int or unsigned int for the natural word size of the machine? – Mathieu JVL Jul 06 '12 at 07:19
  • 5
    @MathieuJVL: depends on what you call the natural word size. A machine may operate fastest on 32-bit integers, but have 64-bit pointers so `size_t` must be larger than `int`. – Fred Foo Jul 27 '12 at 21:27
  • 2
    You should actually use uint_fast32_t in that case. This is defined as the "fastest" data type that is at least 32-bits wide. It's a bit harder to type than unsigned, though. – Ethan Jun 16 '13 at 22:51
  • 3
    Note that strictly speaking, all the `intN_t` and `uintN_t` types are optional. For example, there used to be machines with 36-bit words, and `CHAR_BIT == 9`. For such machines, there was no 8-bit type, so `uint8_t` would not be defined on them (but `uint_least8_t` would be defined, as would `uint_fast8_t`). Such machines are mainly museum pieces these days, but another, modern type of system that would not provide `uint8_t` would be a DSP chip where `CHAR_BIT == 16`. – Jonathan Leffler Aug 13 '14 at 22:25
  • Good answer, just a suggestion to improve it: If you use `int` instead of `unsigned` it not only be shorter but would fit better to the integer literals (because these are signed per default). This suggestion would differ for using `sizeof` in the condition, but then it wouldn't show when to use "the native type". – Wolf Sep 13 '17 at 08:58
  • @Wolf - Reasonable suggestion. Done. – Nemo Sep 14 '17 at 04:08
  • Re `"char" or "unsigned char" if you mean "byte"` - no, use `uint8_t` - please consider editing this answer. @JonathanLeffler gave good reasons in his comment above. Moreover, `uint8_t` suggests "this is an 8-bit thing", which should not be considered signed. `char` suggests "this is a character" (ASCII), which is often not the case. – cp.engr Aug 31 '18 at 17:02
  • @cp.engr - You are mistaken. `char` and `unsigned char` are always 1 byte, but not necessarily 8 bits. Search any C or C++ standard for "byte" and read carefully; e.g. http://eel.is/c++draft/basic.types#2 – Nemo Aug 31 '18 at 17:26
  • @Nemo, semantic point taken that byte and "8-bit thing" are not always synonymous; thanks for that. However, I think this is not widely understood, and (so far) I still standby the ideas of communicating intent, and that developers usually mean to work with 8-bit bytes, so `uint8_t` is the best fit for that. – cp.engr Aug 31 '18 at 18:48
22

You should use both. You should use int, as explained in the other answers, when you need a "reasonably sized" integer. Use char when you need a character: it's self-documenting.

You should use uint32_t and friends when interfacing with the outside world in binary: when doing network programming, handling binary files or using foreign multi-byte encodings, etc. In these cases, the exact size of a type is crucial to writing correct, portable, self-documenting code. That's what <stdint.h> (or C++0x <cstdint>) is for.

(Endianness is equally crucial, but that's another matter entirely.)

Fred Foo
  • 328,932
  • 68
  • 689
  • 800
4

It depends on the purpose of the variable.

If you need a loop counter, use int. If you need a string, use an array of char.

If you need a numeric variable that can hold -1 to 100, int8_t is good. If you need to represent a value from 0 to 100,000 then uint32_t uint_least32_t (thanks @Serge) is an excellent choice.

Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
  • 3
    "If you need to represent a value from 0 to 100,000 then uint32_t is an excellent choice." No. `uint_least32_t` is excellent choice. And `long int` is good choice. `uint_least32_t` is always present in compatible implementation. `uint32_t` is not required to implement (and for some platforms it may be impossible to implement it efficiently). – Serge Dundich May 26 '11 at 20:46
  • 1
    Actually `uint32_t` to be used if you need exact size type. E.g. if you need to define a portable binary structure (to define some structures for binary formats like filesystems/databases/multimedia containers, some protocol headers, etc). – Serge Dundich May 26 '11 at 20:51
  • "*If you need a numeric variable that can hold -1 to 100, `int8_t` is good."* Not particularly. For a single variable, `int` is likely to be better; `int8_t` is likely to save 3 or so bytes, but that saving will be swamped by the performance hit of extracting a single byte which is likely to be promoted to `int` anyway. You could use `intfast8_t` if you wanted to be pedantic about it, but really, just use `int`. `int8_t` might be better if you need a huge array of them. – Keith Thompson Aug 16 '13 at 21:58
  • @Keith: For a local (automatic/stack) variable, `int8_t` probably performs poorly. I intended to cover that case with the loop counter example. For variables inside data structures, the better cache efficiency of the `int8_t` almost always more than compensates for any extra work to extract the single byte. – Ben Voigt Aug 17 '13 at 00:15
  • I'd suggest *If you need an **ANSI** string, use an array of char.* (addition emphasised) – Wolf Sep 13 '17 at 09:08
  • 1
    @Wolf: And if your string isn't ANSI, use UTF-8 and "array of `char`" is still suitable. – Ben Voigt Sep 13 '17 at 21:15
  • @BenVoigt Yes, that's absolutely true. String could as well mean a series of adjacent bytes but gets easily associated with human readable text... – Wolf Sep 14 '17 at 07:33
3

One particular situation in which you'll need to use the typedefs from cstdint is when dealing with code that does a lot of pointer-to-int conversions, in which case using intptr_t is an absolute requirement.

In the company I work for, we are preparing ourselves to migrate from 32bits to 64bits tons of poor-quality C/C++ code that keep casting pointers to int and then back to pointers, which will definitely fail on 64bits architectures, so we'll attempt to sanitize the code whenever possible (i.e. modify data structures and interfaces to remove the need for casts entirely) and use intptr_t instead of int everywhere else.

On a side note: casting in general should raise suspicion, but seriously, casting pointers to integers is almost always the consequence of a serious flaw somewhere in your design. Basically, you're lying to the compiler, the platform and more importantly your co-workers every time you hide a pointer behind an int.

Other than that, like others said: use generic types when possible and type of explicit size when required.

ZeRemz
  • 1,578
  • 12
  • 15
  • 1
    +1. Although... Once upon a time, long ago, "long" was supposed to be long enough to hold a pointer; indeed, long enough to hold any signed integral type. Then "long long" came along and messed that up. How I long for the old days... – Nemo May 26 '11 at 22:22
  • @Nemo: Nope, holding a pointer has never been the purpose of `long`. – Ben Voigt May 27 '11 at 00:39
  • @Ben: Maybe not. But "long", once upon a time, was supposed to be the "longest integral type", with semantics not unlike intmax_t. (No, I do not have a reference. And I am not claiming this was ever standard. But I do remember the hue and cry from various experts when GCC first introduced "long long".) So if any pointer could ever be cast to int, it could also be cast to long... – Nemo May 27 '11 at 01:04
  • @Nemo: That last sentence is correct, because by definition `sizeof (long) >= sizeof (int)`. But casting pointers to non-pointer types has never been portable. – Ben Voigt May 27 '11 at 02:10
  • 1
    Rather than changing the code to use `intptr_t` (or `uintptr_t`), can you change the code so it doesn't need to convert between pointers and integers at all? If you need a generic pointer type, you can use `void*`. – Keith Thompson Aug 16 '13 at 21:59
1

You basically seems to not have any clue between uint32_t and unsigned int. This is perfectly normal, as you don't necessarily know how your type will be used later.

Just use a typedef.

Regardless of if you need a unsigned int or a uint32_t (which you can think of later, when you have a more complete view of what your program will be), using a typedef will help you make your code clearer by specifying what you are really manipulating, and it will make it more easy to change to another type when you figure out months after that your initial choice was the worst one. There isn't a "right answer" here, because you usually figure those things out the hard way. Interoperating between some library that wants uint32_t and some other library that wants ints is painful.

BatchyX
  • 4,656
  • 2
  • 16
  • 17
  • 2
    The first sentence is needlessly insulting. The rest of this answer is quite reasonable though. – Ben Voigt May 27 '11 at 00:38
  • it wasn't meant to be insulting. is that better now ? – BatchyX May 27 '11 at 17:22
  • 5
    Unfortunately it's not. "You don't have any clue" is an idiomatic expression in English, it means "You don't know what you're talking about, you are stupid/uneducated/an imbecile." I think what you meant is "You haven't explained yourself" which could be worded as "You haven't provided any clue". For more detail, it would be best to ask a question on the English usage subsite. – Ben Voigt May 27 '11 at 22:14
-2

Use templates and generic programming wherever possible. Don't rely on any types if you don't have to!

If you have a function that takes a number and returns it multiplied by 2, write it like this:

template <typename Number>
inline Number doubleNum(Number n) {
  return 2*n;
}

Or even this:

template <typename RetType, typename NumberType>
inline RetType doubleNum(NumberType n) {
  return 2*n;
}

This way, if you have a library that uses ints, doubles, uint64_ts - you name it - you can work with it, without rewriting your code. If you need to work with binary files or network programming, you can work with fixed-size types, without rewriting your code. And if you need arbitrary precision numbers, you can work with a class which implements the same interface as a primitive integer or float type via operator overloading, such as a GMP wrapper, without rewriting your code.

And you can specialize templated functions or classes, to optimize specific cases or work with classes (or C structs) that don't conform to the relevant interface:

/*
 * optimization:
 * primitive integers can be bit-shifted left to
 * achieve multiplication by powers of 2
 * (note that any decent compiler will do this
 *  for you, behind the hood.)
 */
template <>
inline int doubleNum<int>(int n) {
  return n<<1;
}
/*
 * optimization:
 * use assembly code
 */
template <>
inline int32_t doubleNum<int32_t>(int32_t n) {
  asm {
    ...
  }
}
/*
 * work with awkward number class
 */
template <>
inline AwkwardNumber doubleNum<AwkwardNumber>(AwkwardNumber n) {
  n.multiplyBy(2);
  return n;
}