Should I use cstdint?
Asked Answered
W

6

45

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?

Wifely answered 26/5, 2011 at 20:30 Comment(12)
These types aren't equivalent.Tellurian
To me it is about a million times less intuitive.Eulogize
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.Flews
@Thomas: uint32_t and friends are POD; they are typedef's inherited from C99.Belonging
I'm terming POD as classical int, unsigned int, char, etc.Flews
@Thomas : POD already has a defined meaning; those are called 'primitive' or 'fundamental' types.Excerpta
@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.Ludhiana
@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'.Excerpta
@BenVoigt : Oh, and what camp does long long fall into? ;-PExcerpta
@ildjarn: "recently standardized gcc extension"?Ludhiana
@ildjarn: and then there's the recently standardized msvc extension, nullptr_tLudhiana
@BenVoigt : I like how it's defined in VC++ 2010 -- typedef decltype(nullptr) nullptr_t;.Excerpta
N
41

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.

Neall answered 26/5, 2011 at 20:37 Comment(9)
Wouldn't use size_t instead of int or unsigned int for the natural word size of the machine?Gourley
@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.Belonging
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.Erfert
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.Azotic
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".Hamill
@Hamill - Reasonable suggestion. Done.Neall
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.Ramey
@Ramey - 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. eel.is/c++draft/basic.types#2Neall
@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.Ramey
B
24

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.)

Belonging answered 26/5, 2011 at 20:41 Comment(1)
+1 for true purpose of exact-sized types and notes about self-documented coding.Crinoline
L
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.

Ludhiana answered 26/5, 2011 at 20:34 Comment(7)
"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).Crinoline
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).Crinoline
"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.Ma
@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.Ludhiana
I'd suggest If you need an ANSI string, use an array of char. (addition emphasised)Hamill
@Wolf: And if your string isn't ANSI, use UTF-8 and "array of char" is still suitable.Ludhiana
@BenVoigt Yes, that's absolutely true. String could as well mean a series of adjacent bytes but gets easily associated with human readable text...Hamill
M
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.

Maisonette answered 26/5, 2011 at 20:50 Comment(5)
+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...Neall
@Nemo: Nope, holding a pointer has never been the purpose of long.Ludhiana
@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...Neall
@Nemo: That last sentence is correct, because by definition sizeof (long) >= sizeof (int). But casting pointers to non-pointer types has never been portable.Ludhiana
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*.Ma
H
2

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.

Herminiahermione answered 26/5, 2011 at 21:23 Comment(3)
The first sentence is needlessly insulting. The rest of this answer is quite reasonable though.Ludhiana
it wasn't meant to be insulting. is that better now ?Herminiahermione
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.Ludhiana
P
-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;
}
Passionless answered 23/1, 2012 at 3:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.