TL;DR
The behavior is valid and such compilers/architectures do exist
- TI C5500/C6000 with 4-byte
int
, 5-byte long
- Motorola DSP5600x/3xx series with 2-byte
short
, 3-byte int
, 6-byte long
- x86 with 8-byte
double
, 10-byte long double
The number of bits used to represent the type long
is not always the same as, or an integer multiple of, the number of bits in the type int
. The ability to represent a greater range of values (than is possible in the type int) may be required, but processor costs may also be a consideration...
Derek M. Jones' The New C Standard (Excerpted material) - An Economic and Cultural Commentary
The other answer have already recapped C++ standard requirements. Similarly C standard also doesn't constrain the type (floating-point or integer) sizes in bytes to powers of 2. The most common example is long double
, which is most often 10 bytes in x86 (with padding to 12 or 16 bytes in many modern compilers).
ISO/IEC 9899:1999 (E)
5.2.4.2.1 Sizes of integer types <limits.h>
- The values given below shall be replaced by constant expressions suitable for use in
#if
preprocessing directives. Moreover, except for CHAR_BIT
and MB_LEN_MAX
, the following shall be replaced by expressions that have the same type as would an expression that is an object of the corresponding type converted according to the integer promotions. Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign. [...]
6.2.5 Types
There are five standard signed integer types, designated as signed char
, short int
, int
, long int
, and long long int
. (These and other types may be designated in several additional ways, as described in 6.7.2.) There may also be implementation-defined extended signed integer types.28)
The standard and extended signed integer types are collectively called signed integer types.29)
For any two integer types with the same signedness and different integer conversion rank (see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a subrange of the values of the other type.
Odd-sized integer types are much rarer, but still exist. Many DSPs have standard-conforming compilers with non-power-or-2 types where int
has 32 bits, long
has 40 bits.
long
is
- 40 bits or 5 bytes for C6000 COFF. This is fully compliant with any major C/C++ standard as those standards are all defining a minimum requirement of 4 byte for long (aka. long int). Programmers are often falsely assuming this type having a size of exactly 4 bytes.
Emphasis mine
C89 Support in TI Compilers#Misunderstandings about TI C
Offside note: On some TI targets even long long
is also a 32 or 40-bit type, which is valid in C89 as an extension but violates C99
Some targets have long long
(an extension from C99), but not a conforming one. C99 requires at least 64 bits, but C2700 has 32-bit long long
, and C5500 has 40-bit long long
. C2800, C6000, and ARM have 64-bit long long
, and C5400 and MSP430 do not support long long
. This is not technically a violation of C89, since this is actually an extension, but if we start supporting C99, this would be a violation of C99 (C99 5.2.4.2.1 "Sizes of integer types <limits.h>" para 1).
The wider type's size doesn't even have to be a multiple of its preceding type's size. Continuing with what Derek M. Jones said in The New C Standard (Excerpted material): An Economic and Cultural Commentary
... For instance, the Texas Instruments TMS320C6000, a DSP processor, uses 32 bits to represent the type int
and 40 bits to represent the type long
(this choice is not uncommon). Those processors (usually DSP) that use 24 bits to represent the type int
, often use 48 bits to represent the type long
. The use of 24/48 bit integer type representations can be driven by application requirements where a 32/64-bit integer type representation are not cost effective.
In all 24-bit DSPs I had known before, CHAR_BIT == 24
and all types have sizes as multiples of 24 bits, but I've just found out that the Motorola DSP5600x/3xx series have a really "strange" type system
Data Type |
size in bits |
(un)signed char |
8 |
(un)signed short |
16 |
(un)signed int |
24 |
(un)signed long |
48 |
(long)_fract |
24 (48) |
pointer |
16/24 |
float/double |
24+8 |
enum |
24 |
So in this case sizeof(char) == 1
and sizeof(short) == 2
but sizeof(int) == 3
and sizeof(long) == 6
Unfortunately GCC calls them (long
and long long
) double-word integers, and so do most people, making a big misunderstanding, although it doesn't necessarily be double the size.