Declaring fixed-size integer typedef in Standard C

2019-02-18 01:04发布

问题:

Is there a reliable way to declare typedefs for integer types of fixed 8,16,32, and 64 bit length in ISO Standard C?

When I say ISO Standard C, I mean that strictly:

  • ISO C89/C90, not C99.
  • No headers not defined in the ISO standard.
  • No preprocessor symbols not defined in the ISO standard.
  • No type-size assumptions not specified in the ISO standard.
  • No proprietary vendor symbols.

I see other questions similar to this in StackOverflow, but no answers yet that do not violate one of the above constraints. I'm not sure it's possible without resorting to platform symbols.

回答1:

Yes you can.

The header file limits.h should be part of C90. Then I would test through preprocessor directives values of SHRT_MAX, INT_MAX, LONG_MAX, and LLONG_MAX and set typedefs accordingly.

Example:

#include <limits.h>

#if SHRT_MAX == 2147483647
typedef unsigned short int uint32_t;
#elif INT_MAX == 2147483647
typedef unsigned int uint32_t;
#elif LONG_MAX == 2147483647
typedef unsigned long uint32_t ;
#elif LLONG_MAX == 2147483647
typedef unsigned long long uint32_t;
#else
#error "Cannot find 32bit integer."
#endif


回答2:

Strictly speaking, ISO 9899:1999 superceded ISO 9899:1990 so is the only current ISO standard C language specification.

As exact width typedef names for integer types were only introduced into the standard in the 1999 version, what you want is not possible using only the 1990 version of the standard.



回答3:

There is none. There is a reliable way to declare individual integer variables up to 32 bits in size, however, if you're willing to live with some restrictions. Just use long bitfields (the latter is guaranteed to be at least 32-bit wide, and you're allowed to use up to as many bits in a bitfields as would fit in the variable if bitfield declarator was omitted). So:

struct {
   unsigned long foo : 32; 
} bar;

Obviously, you get all the limitations that come with that, such as inability to have pointers to such variables. The only thing this really buys you is guaranteed wraparound at the specified boundary on over/underflow, and even then only for unsigned types, since overflow is undefined for signed.

Aside from that, there's no portable way to do this in pure C90. Among other things, a conformant C90 implementation need not even have a 8-bit integer, for example - it would be entirely legal to have a platform in which sizeof(char) == sizeof(short) == sizeof(int) == 1 and CHAR_BIT == 16 (i.e. it has a 16-bit machine word, and cannot address individual bytes). I've heard that such platforms do in fact exist in practice in form of some DSPs.



回答4:

No, you can't do that.

Now, if you want to count a multi-stage configuration process like Gnu configure as a solution, you can do that and stick to C89. And there are certainly various types you can use that are in C89, and that will DTRT on almost every implementation that's around today, so you get the sizes you want and stick with pure conforming C89. But the bit widths, while what you want, will not in general be specified by the standard.



回答5:

A danger with such approaches when using modern compilers is that it has become fashionable for compilers to assume that a pointer of one integer type will not be used to access values of another, even when both types have the same size and representation. If two types have the same size and representation, and two parts of the same program each choose one of them, applying link-time optimizations to programs that share pointers to such data could result in improper behavior. For some implementations on many systems, there will be at least one size of integer for which it will be impossible to declare a pointer which can be safely used to access all integer values of that size; e.g. on systems where both long and long long are 64 bits, there will be no way to declare a pointer which can be used reliably to access data of either type interchangeably.