ÜberWall

C sizes

Article by khorben on 08/04/2006 17:44:41
Why the fuzz?

If you have the chance to be an experienced C programmer, or otherwise be familiar with the C language definition, you may be aware of this part of the ANSI C standard [1]:

5.2.4.2.1 Sizes of integer types <limits.h>

[#1] 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.

And to be complete:

6.3.1.1 Boolean, characters, and integers

[#1] Every integer type has an integer conversion rank
defined as follows:

-- No two signed integer types shall have the same rank,
even if they have the same representation.

-- The rank of a signed integer type shall be greater than
the rank of any signed integer type with less
precision.

-- The rank of long long int shall be greater than the *
rank of long int, which shall be greater than the rank
of int, which shall be greater than the rank of short
int, which shall be greater than the rank of signed
char.

The last sentence of the first excerpt means, if I am correct, that essential scalar types in C have an undetermined size. They just have to hold at least:
Platform |char |short |long |long |int |float |double|Notes
| | | |long | | | |
ANSI C |8 |16 |32 |64 |16 |32 |64 |



Comparison chart

The following table contains the actual values we have observed on the architectures we have come across:
Reminder: do NOT rely on results from this table
Platform |char |short |long |long |int |float |double|Notes
| | | |long | | | |
FreeBSD 6/gcc/x86 |8 |16 |32 |64 |32 |32 |64 |
Linux/gcc/x86 |8 |16 |32 |64 |32 |32 |64 |
Linux/gcc/x86-64 |8 |16 |64 |64 |32 |32 |64 |
NetBSD/gcc/sparc |8 |16 |32 |64 |32 |32 |64 |
NetBSD/gcc/sparc64|8 |16 |64 |64 |32 |32 |64 |
Solaris 10 |8 |16 |32 |64 |32 |32 |64 |Defaults to
/gcc/sparc64 | | | | | | | |ELF 32-bit
Solaris 10 |8 |16 |64 |64 |32 |32 |64 |With -m64
/gcc/sparc64 | | | | | | | |ELF 64-bit
Windows NT 5.0 |8 |16 |32 |64 |32 |32 |64 |
/Dev-C++/x86-64 | | | | | | | |

If you think you can contribute interesting results, you can use the following C program to help:
#include <stdio.h>
#define type(a) printf("sizeof(%s) = %u\\n", "" # a, sizeof(a) * 8);
int main(void)
{
type(char);
type(short);
type(long);
type(long long);
type(int);
type(float);
type(double);
return 0;
}

Note that this program may indeed trigger warnings on sizeof() result size.

Send your results and reactions to khorben [2]. Feel free to send results for any platform unlisted.

Conclusion

What's the morale in this? Think twice before trusting the sizes of the basic data types in C: you may use <stdint.h> instead.

[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/text/n843.txt
[2] mailto:khorben awt uberwall dowt org