<Cstdint> VS <Stdint.H>

cstdint vs stdint.h

The original intention in C++98 was that you should use <cstdint> in C++, to avoid polluting the global namespace (well, not <cstdint> in particular, that's only added in C++11, but the <c*> headers in general).

However, implementations persisted in putting the symbols into the global namespace anyway, and C++11 ratified this practice[*]. So, you basically have three options:

  • Use <cstdint> and either fully qualify each integer type you use or else bring it into scope with using std::int32_t; etc (annoying because verbose, but it's the right way to do it just like for any other symbol in the C++ standard library)
  • Use <stdint.h> (slightly bad because deprecated)
  • Use <cstdint> and assume your implementation will put the symbols in the global namespace (very bad because not guaranteed).

In practice I suspect that an annoying large amount of code uses the last option, simply because it's easy to do by accident on an implementation where <cstdint> puts the symbols in the global namespace. You should try to use the first. The second has one virtue, that it is guaranteed to put stuff in the global namespace instead of only maybe doing it. I don't think that's particularly useful, but it might save some typing if that's your priority.

There's a fourth option, #include <cstdint> followed by using namespace std; which is sometimes useful but there are places that you shouldn't put the using namespace std;. Different people will have different ideas where those places are, but "at top level in a header file" is worse than "at top level in a cpp file", which is worse than "in a limited scope". Some people never write using namespace std; at all.

[*] That means C++ standard headers are permitted to put stuff in the global namespace but not required to. So you have to avoid colliding with those symbols, but you can't actually use them because they might not be there. Basically, the global namespace in C++ is a minefield, try to avoid it. One might argue that the committee has ratified a practice by implementations that is nearly as harmful as sticking using namespace std; at top level in a header file -- the difference being that the implementations only do it for symbols in the C standard library, whereas using namespace std; does it for C++-only symbols too. There's a section in the C standard that lists names reserved for future additions to the standard. It's not a completely stupid idea to treat those names as reserved in the C++ global namespace too, but it's not essential.

stdint.h or standard types?

First off, Microsoft's implementation does support <stdint.h>.

Use the appropriate type for what you're doing.

If you need, for example, an unsigned type that's exactly 16 bits wide with no padding bits, use uint16_t, defined in <stdint.h>.

If you need an unsigned type that's at least 16 bits wide, you can use uint_least16_t, or uint_fast16_t, or short, or int.

You probably don't need exact-width types as often as you think you do. Very often what matters is not the exact size of a type, but the range of values it supports. But exact representation is important when you're interfacing to some externally defined data format. In that case, you should already have declarations that tell you what types to use.

There are specific requirements on the ranges of the predefined types: char is at least 8 bits, short and int are at least 16 bits, long is at least 32 bits, and long long is at least 64 bits. Also, short is at least as wide as char, int is at least as wide as short, and so forth. (The standard specifies minimum ranges, but the minimum sizes can be derived from the ranges and the fact that a binary representation is required.)

Note that <stdint.h> is a C header. If you #include it in a C++ program, the type names will be imported directly into the global namespace, and may or may not also be imported into the std namespace. If you #include <cstdint>, then the type names will be imported into the std namespace, and may or may not also be imported into the global namespace. Macro names such as UINT32_MAX are not in any namespace; they're always global. You can use either version of the header; just be consistent about using or not using the std:: prefix.

Is there a reason to use std::int64_t from cstdint / cinttypes over int64_t from stdint.h / inttypes.h ?

In C++, the "C library" headers of the form <foo.h> are deprecated, and headers <cfoo> should be used instead. (I put "C library" in quotes because that's just a name for this part of the C++ standard library. Everything we're talking about is part of the C++ standard library, nothing has actually to do with C.)

Whether you should use the non-deprecated headers depends on your personal attitude towards deprecation.

(The amount of physical typing the code author has to do is a very poor concern. Code is read and maintained far more than it is written. The latter are important concerns, the former mostly a distraction.)

Use types in cstdint with or without namespace

Prefer names declared in the std namespace. The reason is given in §17.6.1.3/4 (ISO/IEC 14882:2011(E), C++11):

Except as noted in Clauses 18 through 30 and Annex D, the contents of each header cname shall be the same as that of the corresponding header name.h, as specified in the C standard library (1.2) or the C Unicode TR, as appropriate, as if by inclusion. In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the namespace std. It is unspecified whether these names are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations (7.3.3).

If you use the names from the <cname> headers without std, your program is relying on unspecified requirements.

This was different in C++03 and earlier where names were only supposed to appear in the std namespace. However, the reality was that many implementations were simply injecting the contents of the C standard library headers <name.h> into std and so this was accommodated for in C++11. The corresponding section (§17.4.1.2/4) from the C++03 standard says:

Except as noted in clauses 18 through 27, the contents of each header cname shall be the same as that of the corresponding header name.h, as specified in ISO/IEC 9899:1990 Programming Languages C (Clause 7), or ISO/IEC:1990 Programming Languages—C AMENDMENT 1: C Integrity, (Clause 7), as appropriate, as if by inclusion. In the C++ Standard Library, however, the declarations and definitions (except for names which are defined as macros in C) are within namespace scope (3.3.5) of the namespace std.

Further to this, qualifying names with std:: helps to avoid collisions - you know exactly what you're getting if you fully qualify it. If you're really going to do using namespace std or using std::something, at least do it in as minimal a scope as you can.

Why do stdint.h can be found but cstdint not?

Your compiler supports the old C++ standard which has no <cstdint> (as C90 had no <stdint.h>). <cstdint> is new to C++11.

difference between stdint.h and inttypes.h

See the wikipedia article for inttypes.h.

Use stdint.h for a minimal set of definitions; use inttypes.h if you also need portable support for these in printf, scanf, et al.

Calling C++ standard header (cstdint) from C file

The c prefix in cstdint is because it's really a header file incorporated from C. The name in C is stdint.h.

You need to conditionally include the correct header by detecting the __cplusplus macro. You also need this macro to use the extern "C" part, as that's C++ specific:

#ifndef OUTPUT_FROM_CPP_H
#define OUTPUT_FROM_CPP_H

#ifdef __cplusplus
// Building with a C++ compiler
# include <cstdint>
extern "C" {
#else
// Building with a C compiler
# include <stdint.h>
#endif

uint8_t myCppFunction(uint8_t n);

#ifdef __cplusplus
} // Match extern "C"
#endif

#endif

cstdint vs std::size_t types

The C++11 standard (section 18.2) says:

(5). The type ptrdiff_t is an implementation-defined signed integer type that can hold the difference of two subscripts in an array object....

(6). The type size_t is an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.

(7). [Note: It is recommended that implementations choose types for ptrdiff_t and size_t whose integer conversion ranks (4.13) are no greater than that of signed long int unless a larger size is necessary to contain all the possible values. —end note]

From this we see that:

size_t is specifically for byte-sizes of objects, and its companion ptrdiff_t is specifically for math with array indices. uintmax_t, on the other hand, is the largest unsigned integral type.

Depending on the platform uintmax_t could be larger than size_t.

We also know that:

sizeof returns a size_t, and the STL container size_types are typically identical to size_t, so it makes sense to use size_t in code that deals with sizeof or STL containers.

Now mix in the fact the <cstdint> is new-ish to C++, and I think it's pretty clear why established libraries like Boost have been using size_t.

c++ std:: types instead of corresponding c types

So is there a need to use say std::int8_t instead of the short C form int8_t somewhere?

Yes. Per [headers]/4 it is unspecified if the C names are defined in the global namespace or not. Because of this you need std::int8_t to use int8_t. It doesn't guarantee that std::int8_t exists, just that if it does exist, using std::int8_t is guaranteed to work.

You can just add using std::int8_t; so you don't have to type std::int8_t all over the place.


The relevant part of [headers]/4 is

In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope of the namespace std

emphasis mine

This means we know they are declared in std::. The part

It is unspecified whether these names (including any overloads added in [language.support] through [thread] and [depr]) are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations ([namespace.udecl]).

then allows the names to be in the global namespace, but does required it. This lets implementations do things like

<cstdint>:

namespace std
{
#include <stdint.h>
}

or

<cstdint>:

#include <stdint.h>
namespace std
{
using ::int8_t;
using ::int16_t;
//...
}

Bot accomplish the same thing (putting the names in std::) and are legal implementations but only the second one puts the names in the global scope.



Related Topics



Leave a reply



Submit